@@ -28,6 +28,8 @@ import (
2828 "cloud.google.com/go/internal/trace"
2929 sppb "cloud.google.com/go/spanner/apiv1/spannerpb"
3030 "github.com/googleapis/gax-go/v2"
31+ "go.opentelemetry.io/otel/attribute"
32+ "go.opentelemetry.io/otel/metric"
3133 "google.golang.org/api/iterator"
3234 "google.golang.org/api/option"
3335 "google.golang.org/api/option/internaloption"
@@ -104,6 +106,7 @@ type Client struct {
104106 ct * commonTags
105107 disableRouteToLeader bool
106108 dro * sppb.DirectedReadOptions
109+ otConfig * openTelemetryConfig
107110}
108111
109112// DatabaseName returns the full name of a database, e.g.,
@@ -112,6 +115,11 @@ func (c *Client) DatabaseName() string {
112115 return c .sc .database
113116}
114117
118+ // ClientID returns the id of the Client. This is not recommended for customer applications and used internally for testing.
119+ func (c * Client ) ClientID () string {
120+ return c .sc .id
121+ }
122+
115123// ClientConfig has configurations for the client.
116124type ClientConfig struct {
117125 // NumChannels is the number of gRPC channels.
@@ -192,6 +200,23 @@ type ClientConfig struct {
192200 // and ExecuteSqlRequests for the Client which indicate which replicas or regions
193201 // should be used for non-transactional reads or queries.
194202 DirectedReadOptions * sppb.DirectedReadOptions
203+
204+ OpenTelemetryMeterProvider metric.MeterProvider
205+ }
206+
207+ type openTelemetryConfig struct {
208+ meterProvider metric.MeterProvider
209+ attributeMap []attribute.KeyValue
210+ otMetricRegistration metric.Registration
211+ openSessionCount metric.Int64ObservableGauge
212+ maxAllowedSessionsCount metric.Int64ObservableGauge
213+ sessionsCount metric.Int64ObservableGauge
214+ maxInUseSessionsCount metric.Int64ObservableGauge
215+ getSessionTimeoutsCount metric.Int64Counter
216+ acquiredSessionsCount metric.Int64Counter
217+ releasedSessionsCount metric.Int64Counter
218+ gfeLatency metric.Int64Histogram
219+ gfeHeaderMissingCount metric.Int64Counter
195220}
196221
197222func contextWithOutgoingMetadata (ctx context.Context , md metadata.MD , disableRouteToLeader bool ) context.Context {
@@ -245,6 +270,7 @@ func NewClientWithConfig(ctx context.Context, database string, config ClientConf
245270 if err != nil {
246271 return nil , err
247272 }
273+
248274 if hasNumChannelsConfig && pool .Num () != config .NumChannels {
249275 pool .Close ()
250276 return nil , spannerErrorf (codes .InvalidArgument , "Connection pool mismatch: NumChannels=%v, WithGRPCConnectionPool=%v. Only set one of these options, or set both to the same value." , config .NumChannels , pool .Num ())
@@ -276,16 +302,29 @@ func NewClientWithConfig(ctx context.Context, database string, config ClientConf
276302 if config .Compression == gzip .Name {
277303 md .Append (requestsCompressionHeader , gzip .Name )
278304 }
305+
279306 // Create a session client.
280307 sc := newSessionClient (pool , database , config .UserAgent , sessionLabels , config .DatabaseRole , config .DisableRouteToLeader , md , config .BatchTimeout , config .Logger , config .CallOptions )
281308
309+ // Create a OpenTelemetry configuration
310+ otConfig , err := createOpenTelemetryConfig (config .OpenTelemetryMeterProvider , config .Logger , sc .id , database )
311+ if err != nil {
312+ // The error returned here will be due to database name parsing
313+ return nil , err
314+ }
315+ // To prevent data race in unit tests (ex: TestClient_SessionNotFound)
316+ sc .mu .Lock ()
317+ sc .otConfig = otConfig
318+ sc .mu .Unlock ()
319+
282320 // Create a session pool.
283321 config .SessionPoolConfig .sessionLabels = sessionLabels
284322 sp , err := newSessionPool (sc , config .SessionPoolConfig )
285323 if err != nil {
286324 sc .close ()
287325 return nil , err
288326 }
327+
289328 c = & Client {
290329 sc : sc ,
291330 idleSessions : sp ,
@@ -298,6 +337,7 @@ func NewClientWithConfig(ctx context.Context, database string, config ClientConf
298337 ct : getCommonTags (sc ),
299338 disableRouteToLeader : config .DisableRouteToLeader ,
300339 dro : config .DirectedReadOptions ,
340+ otConfig : otConfig ,
301341 }
302342 return c , nil
303343}
@@ -384,6 +424,7 @@ func (c *Client) Single() *ReadOnlyTransaction {
384424 t .txReadOnly .qo .DirectedReadOptions = c .dro
385425 t .txReadOnly .ro .DirectedReadOptions = c .dro
386426 t .ct = c .ct
427+ t .otConfig = c .otConfig
387428 return t
388429}
389430
@@ -409,6 +450,7 @@ func (c *Client) ReadOnlyTransaction() *ReadOnlyTransaction {
409450 t .txReadOnly .qo .DirectedReadOptions = c .dro
410451 t .txReadOnly .ro .DirectedReadOptions = c .dro
411452 t .ct = c .ct
453+ t .otConfig = c .otConfig
412454 return t
413455}
414456
@@ -479,6 +521,7 @@ func (c *Client) BatchReadOnlyTransaction(ctx context.Context, tb TimestampBound
479521 t .txReadOnly .qo .DirectedReadOptions = c .dro
480522 t .txReadOnly .ro .DirectedReadOptions = c .dro
481523 t .ct = c .ct
524+ t .otConfig = c .otConfig
482525 return t , nil
483526}
484527
@@ -512,6 +555,7 @@ func (c *Client) BatchReadOnlyTransactionFromID(tid BatchReadOnlyTransactionID)
512555 t .txReadOnly .qo .DirectedReadOptions = c .dro
513556 t .txReadOnly .ro .DirectedReadOptions = c .dro
514557 t .ct = c .ct
558+ t .otConfig = c .otConfig
515559 return t
516560}
517561
@@ -616,6 +660,7 @@ func (c *Client) rwTransaction(ctx context.Context, f func(context.Context, *Rea
616660 t .wb = []* Mutation {}
617661 t .txOpts = c .txo .merge (options )
618662 t .ct = c .ct
663+ t .otConfig = c .otConfig
619664
620665 trace .TracePrintf (ctx , map [string ]interface {}{"transactionSelector" : t .getTransactionSelector ().String ()},
621666 "Starting transaction attempt" )
@@ -877,6 +922,9 @@ func (c *Client) BatchWriteWithOptions(ctx context.Context, mgs []*MutationGroup
877922 trace .TracePrintf (ct , nil , "Error in recording GFE Latency. Try disabling and rerunning. Error: %v" , err )
878923 }
879924 }
925+ if metricErr := recordGFELatencyMetricsOT (ct , md , "BatchWrite" , c .otConfig ); metricErr != nil {
926+ trace .TracePrintf (ct , nil , "Error in recording GFE Latency through OpenTelemetry. Error: %v" , err )
927+ }
880928 return stream , rpcErr
881929 }
882930
0 commit comments