Skip to content

Commit

Permalink
feat: client metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
surbhigarg92 committed Jun 19, 2024
1 parent e00b884 commit 624b891
Show file tree
Hide file tree
Showing 8 changed files with 1,002 additions and 9 deletions.
50 changes: 44 additions & 6 deletions google-cloud-spanner/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
<site.installationModule>google-cloud-spanner</site.installationModule>
<opencensus.version>0.31.1</opencensus.version>
<spanner.testenv.config.class>com.google.cloud.spanner.GceTestEnvConfig</spanner.testenv.config.class>
<spanner.testenv.instance>projects/gcloud-devel/instances/spanner-testing-east1</spanner.testenv.instance>
<spanner.gce.config.project_id>gcloud-devel</spanner.gce.config.project_id>
<spanner.testenv.kms_key.name>projects/gcloud-devel/locations/us-east1/keyRings/cmek-test-key-ring/cryptoKeys/cmek-test-key</spanner.testenv.kms_key.name>
<spanner.testenv.instance>projects/span-cloud-testing/instances/surbhi-testing</spanner.testenv.instance>
<spanner.gce.config.project_id>span-cloud-testing</spanner.gce.config.project_id>
<spanner.testenv.kms_key.name>projects/span-cloud-testing/locations/us-east1/keyRings/cmek-test-key-ring/cryptoKeys/cmek-test-key</spanner.testenv.kms_key.name>
</properties>


Expand Down Expand Up @@ -413,17 +413,14 @@
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-common</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-metrics</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
Expand All @@ -435,6 +432,47 @@
<artifactId>opentelemetry-sdk-testing</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.cloud.opentelemetry</groupId>
<artifactId>detector-resources-support</artifactId>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-monitoring</artifactId>
<version>3.38.0</version>
<exclusions>
<exclusion>
<!-- using the perfmark version in opencensus -->
<groupId>io.perfmark</groupId>
<artifactId>perfmark-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.api.grpc</groupId>
<artifactId>proto-google-cloud-monitoring-v3</artifactId>
<version>3.38.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
<dependency>
<groupId>com.google.cloud.opentelemetry</groupId>
<artifactId>shared-resourcemapping</artifactId>
<version>0.27.0</version>
</dependency>
<dependency>
<groupId>com.google.cloud.opentelemetry</groupId>
<artifactId>exporter-trace</artifactId>
<version>0.25.2</version>
</dependency>
<dependency>
<groupId>com.google.cloud.opentelemetry</groupId>
<artifactId>exporter-metrics</artifactId>
<version>0.27.0</version>
</dependency>
</dependencies>
<profiles>
<profile>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package com.google.cloud.spanner;

import static com.google.cloud.spanner.SpannerMetricsConstant.CLIENT_NAME_KEY;
import static com.google.cloud.spanner.SpannerMetricsConstant.CLIENT_UID_KEY;
import static com.google.cloud.spanner.SpannerMetricsConstant.INSTANCE_CONFIG_ID_KEY;
import static com.google.cloud.spanner.SpannerMetricsConstant.DIRECT_PATH_ENABLED_KEY;
import static com.google.cloud.spanner.SpannerMetricsConstant.LOCATION_ID_KEY;
import static com.google.cloud.spanner.SpannerMetricsConstant.PROJECT_ID_KEY;

import com.google.api.gax.core.GaxProperties;
import com.google.auth.Credentials;
import com.google.cloud.opentelemetry.detection.DetectedPlatform;
import com.google.cloud.opentelemetry.detection.GCPPlatformDetector;
import com.google.common.collect.ImmutableSet;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.metrics.InstrumentSelector;
import io.opentelemetry.sdk.metrics.InstrumentType;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
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.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.annotation.Nullable;

class BuiltInOpenTelemetryMetricsProvider {

private static final Logger logger =
Logger.getLogger(BuiltInOpenTelemetryMetricsProvider.class.getName());

private OpenTelemetry openTelemetry;
public OpenTelemetry getOpenTelemetry(String projectId, @Nullable Credentials credentials) {
if (this.openTelemetry == null) {

// Use custom exporter
MetricExporter metricExporter = null;
try {
metricExporter = SpannerCloudMonitoringExporter.create(projectId, credentials);

SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder();
registerView(sdkMeterProviderBuilder, SpannerMetricsConstant.OPERATION_LATENCY_NAME, SpannerMetricsConstant.OPERATION_LATENCIES_NAME);
registerView(sdkMeterProviderBuilder, SpannerMetricsConstant.ATTEMPT_LATENCY_NAME, SpannerMetricsConstant.ATTEMPT_LATENCIES_NAME);
registerView(sdkMeterProviderBuilder, SpannerMetricsConstant.OPERATION_COUNT_NAME, SpannerMetricsConstant.OPERATION_COUNT_NAME);
registerView(sdkMeterProviderBuilder, SpannerMetricsConstant.ATTEMPT_COUNT_NAME, SpannerMetricsConstant.ATTEMPT_COUNT_NAME);

SdkMeterProvider sdkMeterProvider =
sdkMeterProviderBuilder
.registerMetricReader(PeriodicMetricReader.create(metricExporter))
.build();

this.openTelemetry =
OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).build();
} catch (IOException e) {
logger.log(Level.WARNING, "Unable to get OpenTelemetry object for client side metrics, will skip exporting client side metrics", e);
}
}
return this.openTelemetry;
}

public Map<String, String> getClientAttributes(String projectId) {
Map<String, String> clientAttributes = new HashMap<>();
clientAttributes.put(LOCATION_ID_KEY.getKey(), detectClientLocation());
clientAttributes.put(PROJECT_ID_KEY.getKey(), projectId);
clientAttributes.put(INSTANCE_CONFIG_ID_KEY.getKey(), "us-central1");
clientAttributes.put(DIRECT_PATH_ENABLED_KEY.getKey(), "true");
clientAttributes.put(CLIENT_NAME_KEY.getKey(), "spanner-java/" + GaxProperties.getLibraryVersion(SpannerCloudMonitoringExporterUtils.class));
clientAttributes.put(CLIENT_UID_KEY.getKey(), getDefaultTaskValue());
return clientAttributes;
}

private void registerView(SdkMeterProviderBuilder sdkMeterProviderBuilder, String metricName, String metricViewName) {
InstrumentSelector selector =
InstrumentSelector.builder()
.setName(SpannerMetricsConstant.METER_NAME + '/'
+ metricName)
.setMeterName(SpannerMetricsConstant.GAX_METER_NAME)
.setType(InstrumentType.HISTOGRAM)
.setUnit("ms")
.build();
Set<String> attributesFilter =
ImmutableSet.<String>builder()
.addAll(
SpannerMetricsConstant.COMMON_ATTRIBUTES.stream()
.map(AttributeKey::getKey)
.collect(Collectors.toSet()))
.build();
View view =
View.builder()
.setName(SpannerMetricsConstant.METER_NAME + '/'
+ metricViewName)
.setAggregation(SpannerMetricsConstant.AGGREGATION_WITH_MILLIS_HISTOGRAM)
.setAttributeFilter(attributesFilter)
.build();
sdkMeterProviderBuilder.registerView(selector, view);
}


private String detectClientLocation() {
GCPPlatformDetector detector = GCPPlatformDetector.DEFAULT_INSTANCE;
DetectedPlatform detectedPlatform = detector.detectPlatform();
String region = detectedPlatform.getAttributes().get("cloud.region");
return region;
}


/**
* In most cases this should look like ${UUID}@${hostname}. The hostname will be retrieved from
* the jvm name and fallback to the local hostname.
*/
private String getDefaultTaskValue() {
// Something like '<pid>@<hostname>'
final String jvmName = ManagementFactory.getRuntimeMXBean().getName();
// If jvm doesn't have the expected format, fallback to the local hostname
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 UUID.randomUUID() + "@" + hostname;
}
return UUID.randomUUID() + jvmName;
}
}
Loading

0 comments on commit 624b891

Please sign in to comment.