Skip to content

Commit da74dee

Browse files
authored
feat(spanner): support user-provided OpenTelemetry for client metrics export (#13741)
## Summary Adds **Client Metrics**: a new, opt-in feature that exports Spanner's client-side metrics to a caller-provided OpenTelemetry pipeline (OTLP, Prometheus, any exporter), including on Spanner Omni where the existing Cloud Monitoring metrics are unavailable. Fully opt-in and fully decoupled from the existing built-in (Cloud Monitoring) metrics. Default behavior is unchanged: without a client-metrics provider, nothing new happens, and built-in metrics continue to export to Cloud Monitoring exactly as before. > **Scope note:** This change was split out of a larger PR into two focused PRs. This PR contains **only** the Client Metrics export feature. The optional `endpoint` metric attribute for location-aware routing is a separate follow-up: #13740. ## Two independent features - **Built-in metrics** (existing, unchanged): export to Google Cloud Monitoring. Controlled by `setBuiltInMetricsEnabled` and the `SPANNER_DISABLE_BUILTIN_METRICS` environment variable, exactly as today. Not available on Spanner Omni. - **Client metrics** (new): export the client instruments to a caller-owned OpenTelemetry. Controlled **solely** by `setClientMetricsProvider(...)` — a `CustomOpenTelemetryMetricsProvider` turns it on; `NoopMetricsProvider` (or no provider) turns it off. Works on all instance types, Omni included. The two are decoupled: `setBuiltInMetricsEnabled` and the env var affect only the built-in Cloud Monitoring sink and have no effect on client metrics; the client-metrics provider affects only the caller-owned sink and has no effect on built-in metrics. Under the hood these are the same client instruments, exported under the distinct `spanner/client` namespace — the difference is the export path, not the metrics. ## Emulator handling Client metrics are not recorded when Spanner is pointed at the emulator. To make that reliable when the emulator is configured programmatically (via `setEmulatorHost(...)`) and not only through the `SPANNER_EMULATOR_HOST` environment variable, this PR broadens emulator detection: `SpannerOptions.isEmulatorEnabled()` now also recognizes the builder-configured emulator host, and the connection-check error message is generalized to describe both the environment-variable and programmatic configuration paths. This gates both the client and Cloud Monitoring metrics sinks off against the emulator using a single detection predicate. Called out explicitly here for reviewer visibility. ## Motivation Client metrics currently export only to Cloud Monitoring, which is unavailable on Spanner Omni. Customers running on Omni (or who standardize on their own observability stack) had no way to receive these metrics. This lets them route the metrics to any OpenTelemetry exporter, independently of the built-in Cloud Monitoring configuration. ## API ```java SdkMeterProviderBuilder meterProviderBuilder = SdkMeterProvider.builder(); SpannerMetrics.configureMeterProviderBuilder(meterProviderBuilder); // ... attach your exporter to meterProviderBuilder ... OpenTelemetry otel = OpenTelemetrySdk.builder().setMeterProvider(meterProviderBuilder.build()).build(); SpannerOptions options = SpannerOptions.newBuilder() .setClientMetricsProvider(new CustomOpenTelemetryMetricsProvider(otel)) .build(); ```
1 parent c2ce8c1 commit da74dee

19 files changed

Lines changed: 1679 additions & 97 deletions

java-spanner/.readme-partials.yaml

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -53,29 +53,69 @@ custom_content: |
5353
## Metrics
5454
5555
Cloud Spanner client supports [client-side metrics](https://cloud.google.com/spanner/docs/view-manage-client-side-metrics) that you can use along with server-side metrics to optimize performance and troubleshoot performance issues if they occur.
56-
57-
Client-side metrics are measured from the time a request leaves your application to the time your application receives the response.
56+
57+
Client-side metrics are measured from the time a request leaves your application to the time your application receives the response.
5858
In contrast, server-side metrics are measured from the time Spanner receives a request until the last byte of data is sent to the client.
59-
60-
These metrics are enabled by default. You can opt out of using client-side metrics with the following code:
61-
59+
60+
The default Cloud Monitoring export for these metrics is enabled by default. You can opt out of the default Cloud Monitoring export with the following code:
61+
6262
```
6363
SpannerOptions options = SpannerOptions.newBuilder()
6464
.setBuiltInMetricsEnabled(false)
6565
.build();
6666
```
67-
68-
You can also disable these metrics by setting `SPANNER_DISABLE_BUILTIN_METRICS` to `true`.
69-
70-
> Note: Client-side metrics needs `monitoring.timeSeries.create` IAM permission to export metrics data. Ask your administrator to grant your service account the [Monitoring Metric Writer](https://cloud.google.com/iam/docs/roles-permissions/monitoring#monitoring.metricWriter) (roles/monitoring.metricWriter) IAM role on the project.
67+
68+
You can also disable the default Cloud Monitoring export by setting `SPANNER_DISABLE_BUILTIN_METRICS` to `true`. These controls affect only the Cloud Monitoring export. They do not affect a caller-owned client-metrics export configured with `CustomOpenTelemetryMetricsProvider`.
69+
70+
> Note: Client-side metrics needs `monitoring.timeSeries.create` IAM permission to export metrics data to Cloud Monitoring. Ask your administrator to grant your service account the [Monitoring Metric Writer](https://cloud.google.com/iam/docs/roles-permissions/monitoring#monitoring.metricWriter) (roles/monitoring.metricWriter) IAM role on the project.
71+
72+
#### Exporting client metrics to OpenTelemetry
73+
74+
Client metrics export to a caller-owned OpenTelemetry destination is controlled by a `MetricsProvider`,
75+
set with `SpannerOptions.Builder.setClientMetricsProvider(MetricsProvider)`. The available
76+
providers are:
77+
78+
* `DefaultMetricsProvider` (the default): no caller-owned client-metrics destination is configured.
79+
The built-in Cloud Monitoring export follows `setBuiltInMetricsEnabled` and the
80+
`SPANNER_DISABLE_BUILTIN_METRICS` environment variable. On Spanner Omni, where the Cloud Monitoring
81+
export is not available, the default provider results in no client-metrics export.
82+
* `NoopMetricsProvider`: caller-owned client metrics are explicitly disabled. The Cloud Monitoring
83+
export is controlled separately.
84+
* `CustomOpenTelemetryMetricsProvider`: the same Spanner client instruments are additionally recorded
85+
on an `OpenTelemetry` instance that you provide. You own the metrics pipeline (readers, exporters
86+
and resource). This custom destination is independent of the built-in Cloud Monitoring export on all
87+
instance types, including Spanner Omni (`InstanceType.OMNI`), for which Cloud Monitoring export is
88+
not available.
89+
90+
Client metrics are not recorded when the client runs against the Spanner emulator, regardless of the
91+
configured `MetricsProvider`. gRPC-layer metrics are recorded on a custom destination only when the
92+
provided `OpenTelemetry` instance is an `OpenTelemetrySdk`.
93+
94+
When using `CustomOpenTelemetryMetricsProvider`, it is recommended to register the Spanner
95+
client-metrics views on a dedicated `SdkMeterProviderBuilder` with
96+
`SpannerMetrics.configureMeterProviderBuilder(SdkMeterProviderBuilder)` before creating the
97+
`OpenTelemetry` instance. The views rename the raw instruments, apply the Spanner latency
98+
histogram buckets and restrict the recorded attributes to the supported client-metric labels.
99+
100+
```java
101+
SdkMeterProviderBuilder meterProviderBuilder =
102+
SdkMeterProvider.builder().registerMetricReader(PeriodicMetricReader.create(myExporter));
103+
SpannerMetrics.configureMeterProviderBuilder(meterProviderBuilder);
104+
OpenTelemetry openTelemetry =
105+
OpenTelemetrySdk.builder().setMeterProvider(meterProviderBuilder.build()).build();
106+
SpannerOptions options =
107+
SpannerOptions.newBuilder()
108+
.setClientMetricsProvider(CustomOpenTelemetryMetricsProvider.create(openTelemetry))
109+
.build();
110+
```
71111
72112
## Traces
73-
Cloud Spanner client supports OpenTelemetry Traces, which gives insight into the client internals and aids in debugging/troubleshooting production issues.
113+
Cloud Spanner client supports OpenTelemetry Traces, which gives insight into the client internals and aids in debugging/troubleshooting production issues.
74114
75115
By default, the functionality is disabled. You need to add OpenTelemetry dependencies, enable OpenTelemetry traces and must configure the OpenTelemetry with appropriate exporters at the startup of your application.
76116
77117
See [Configure client-side tracing](https://cloud.google.com/spanner/docs/set-up-tracing#configure-client-side-tracing) for more details on configuring traces.
78-
118+
79119
#### OpenTelemetry Dependencies
80120
81121
If you are using Maven, add this to your pom.xml file
@@ -129,7 +169,7 @@ custom_content: |
129169
130170
Spanner spanner = options.getService();
131171
```
132-
172+
133173
#### OpenTelemetry SQL Statement Tracing
134174
The OpenTelemetry traces that are generated by the Java client include any request and transaction
135175
tags that have been set. The traces can also include the SQL statements that are executed and the
@@ -149,42 +189,42 @@ custom_content: |
149189
#### OpenTelemetry API Tracing
150190
You can enable tracing of each API call that the Spanner client executes with the `enableApiTracing`
151191
option. These traces also include any retry attempts for an API call:
152-
192+
153193
```
154194
SpannerOptions options = SpannerOptions.newBuilder()
155195
.setOpenTelemetry(openTelemetry)
156196
.setEnableApiTracing(true)
157197
.build();
158198
```
159-
199+
160200
This option can also be enabled by setting the environment variable
161201
`SPANNER_ENABLE_API_TRACING=true`.
162202
163203
> Note: The attribute keys that are used for additional information about retry attempts and the number of requests might change in a future release.
164-
165-
#### End-to-end Tracing
166-
204+
205+
#### End-to-end Tracing
206+
167207
In addition to client-side tracing, you can opt in for [end-to-end tracing](https://cloud.google.com/spanner/docs/tracing-overview#end-to-end-side-tracing). End-to-end tracing helps you understand and debug latency issues that are specific to Spanner such as the following:
168208
* Identify whether the latency is due to network latency between your application and Spanner, or if the latency is occurring within Spanner.
169209
* Identify the Google Cloud regions that your application requests are being routed through and if there is a cross-region request. A cross-region request usually means higher latencies between your application and Spanner.
170-
210+
171211
```
172212
SpannerOptions options = SpannerOptions.newBuilder()
173213
.setOpenTelemetry(openTelemetry)
174214
.setEnableEndToEndTracing(true)
175215
.build();
176216
```
177-
217+
178218
Refer to [Configure end-to-end tracing](https://cloud.google.com/spanner/docs/set-up-tracing#configure-end-to-end-tracing) to configure end-to-end tracing and to understand its attributes.
179-
219+
180220
> Note: End-to-end traces can only be exported to [Cloud Trace](https://cloud.google.com/trace/docs).
181-
182-
221+
222+
183223
## Instrument with OpenCensus
184224
185225
> Note: OpenCensus project is deprecated. See [Sunsetting OpenCensus](https://opentelemetry.io/blog/2023/sunsetting-opencensus/).
186226
We recommend migrating to OpenTelemetry, the successor project.
187-
227+
188228
## Migrate from OpenCensus to OpenTelemetry
189229
190230
> Using the [OpenTelemetry OpenCensus Bridge](https://mvnrepository.com/artifact/io.opentelemetry/opentelemetry-opencensus-shim), you can immediately begin exporting your metrics and traces with OpenTelemetry.
@@ -214,4 +254,4 @@ custom_content: |
214254
215255
Update your dashboards and alerts to reflect below changes
216256
* **Metrics name** : `cloud.google.com/java` prefix has been removed from OpenTelemery metrics and instead has been added as Instrumenation Scope.
217-
* **Metrics namespace** : OpenTelmetry exporters uses `workload.googleapis.com` namespace opposed to `custom.googleapis.com` with OpenCensus.
257+
* **Metrics namespace** : OpenTelmetry exporters uses `workload.googleapis.com` namespace opposed to `custom.googleapis.com` with OpenCensus.

java-spanner/README.md

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,17 +155,57 @@ Cloud Spanner client supports [client-side metrics](https://cloud.google.com/spa
155155
Client-side metrics are measured from the time a request leaves your application to the time your application receives the response.
156156
In contrast, server-side metrics are measured from the time Spanner receives a request until the last byte of data is sent to the client.
157157

158-
These metrics are enabled by default. You can opt out of using client-side metrics with the following code:
158+
The default Cloud Monitoring export for these metrics is enabled by default. You can opt out of the default Cloud Monitoring export with the following code:
159159

160160
```
161161
SpannerOptions options = SpannerOptions.newBuilder()
162162
.setBuiltInMetricsEnabled(false)
163163
.build();
164164
```
165165

166-
You can also disable these metrics by setting `SPANNER_DISABLE_BUILTIN_METRICS` to `true`.
166+
You can also disable the default Cloud Monitoring export by setting `SPANNER_DISABLE_BUILTIN_METRICS` to `true`. These controls affect only the Cloud Monitoring export. They do not affect a caller-owned client-metrics export configured with `CustomOpenTelemetryMetricsProvider`.
167167

168-
> Note: Client-side metrics needs `monitoring.timeSeries.create` IAM permission to export metrics data. Ask your administrator to grant your service account the [Monitoring Metric Writer](https://cloud.google.com/iam/docs/roles-permissions/monitoring#monitoring.metricWriter) (roles/monitoring.metricWriter) IAM role on the project.
168+
> Note: Client-side metrics needs `monitoring.timeSeries.create` IAM permission to export metrics data to Cloud Monitoring. Ask your administrator to grant your service account the [Monitoring Metric Writer](https://cloud.google.com/iam/docs/roles-permissions/monitoring#monitoring.metricWriter) (roles/monitoring.metricWriter) IAM role on the project.
169+
170+
#### Exporting client metrics to OpenTelemetry
171+
172+
Client metrics export to a caller-owned OpenTelemetry destination is controlled by a `MetricsProvider`,
173+
set with `SpannerOptions.Builder.setClientMetricsProvider(MetricsProvider)`. The available
174+
providers are:
175+
176+
* `DefaultMetricsProvider` (the default): no caller-owned client-metrics destination is configured.
177+
The built-in Cloud Monitoring export follows `setBuiltInMetricsEnabled` and the
178+
`SPANNER_DISABLE_BUILTIN_METRICS` environment variable. On Spanner Omni, where the Cloud Monitoring
179+
export is not available, the default provider results in no client-metrics export.
180+
* `NoopMetricsProvider`: caller-owned client metrics are explicitly disabled. The Cloud Monitoring
181+
export is controlled separately.
182+
* `CustomOpenTelemetryMetricsProvider`: the same Spanner client instruments are additionally recorded
183+
on an `OpenTelemetry` instance that you provide. You own the metrics pipeline (readers, exporters
184+
and resource). This custom destination is independent of the built-in Cloud Monitoring export on all
185+
instance types, including Spanner Omni (`InstanceType.OMNI`), for which Cloud Monitoring export is
186+
not available.
187+
188+
Client metrics are not recorded when the client runs against the Spanner emulator, regardless of the
189+
configured `MetricsProvider`. gRPC-layer metrics are recorded on a custom destination only when the
190+
provided `OpenTelemetry` instance is an `OpenTelemetrySdk`.
191+
192+
When using `CustomOpenTelemetryMetricsProvider`, it is recommended to register the Spanner
193+
client-metrics views on a dedicated `SdkMeterProviderBuilder` with
194+
`SpannerMetrics.configureMeterProviderBuilder(SdkMeterProviderBuilder)` before creating the
195+
`OpenTelemetry` instance. The views rename the raw instruments, apply the Spanner latency
196+
histogram buckets and restrict the recorded attributes to the supported client-metric labels.
197+
198+
```java
199+
SdkMeterProviderBuilder meterProviderBuilder =
200+
SdkMeterProvider.builder().registerMetricReader(PeriodicMetricReader.create(myExporter));
201+
SpannerMetrics.configureMeterProviderBuilder(meterProviderBuilder);
202+
OpenTelemetry openTelemetry =
203+
OpenTelemetrySdk.builder().setMeterProvider(meterProviderBuilder.build()).build();
204+
SpannerOptions options =
205+
SpannerOptions.newBuilder()
206+
.setClientMetricsProvider(CustomOpenTelemetryMetricsProvider.create(openTelemetry))
207+
.build();
208+
```
169209

170210
## Traces
171211
Cloud Spanner client supports OpenTelemetry Traces, which gives insight into the client internals and aids in debugging/troubleshooting production issues.

0 commit comments

Comments
 (0)