| // Copyright 2017 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 | |
| // | |
| // http://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 spanner | |
| import ( | |
| "context" | |
| "strconv" | |
| "strings" | |
| "sync" | |
| "cloud.google.com/go/spanner/internal" | |
| "go.opencensus.io/stats" | |
| "go.opencensus.io/stats/view" | |
| "go.opencensus.io/tag" | |
| "google.golang.org/grpc/metadata" | |
| ) | |
| const statsPrefix = "cloud.google.com/go/spanner/" | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry for capturing metrics. | |
| var ( | |
| tagKeyClientID = tag.MustNewKey("client_id") | |
| tagKeyDatabase = tag.MustNewKey("database") | |
| tagKeyInstance = tag.MustNewKey("instance_id") | |
| tagKeyLibVersion = tag.MustNewKey("library_version") | |
| tagKeyType = tag.MustNewKey("type") | |
| tagKeyIsMultiplexed = tag.MustNewKey("is_multiplexed") | |
| tagCommonKeys = []tag.Key{tagKeyClientID, tagKeyDatabase, tagKeyInstance, tagKeyLibVersion} | |
| tagNumInUseSessions = tag.Tag{Key: tagKeyType, Value: "num_in_use_sessions"} | |
| tagNumSessions = tag.Tag{Key: tagKeyType, Value: "num_sessions"} | |
| // Deprecated: With InLine Begin transaction client won't maintain separate read or write sessions | |
| tagNumBeingPrepared = tag.Tag{Key: tagKeyType, Value: "num_sessions_being_prepared"} | |
| tagNumReadSessions = tag.Tag{Key: tagKeyType, Value: "num_read_sessions"} | |
| tagNumWriteSessions = tag.Tag{Key: tagKeyType, Value: "num_write_prepared_sessions"} | |
| tagKeyMethod = tag.MustNewKey("grpc_client_method") | |
| // gfeLatencyMetricsEnabled is used to track if GFELatency and GFEHeaderMissingCount need to be recorded | |
| gfeLatencyMetricsEnabled = false | |
| // mutex to avoid data race in reading/writing the above flag | |
| statsMu = sync.RWMutex{} | |
| ) | |
| func recordStat(ctx context.Context, m *stats.Int64Measure, n int64) { | |
| stats.Record(ctx, m.M(n)) | |
| } | |
| var ( | |
| // OpenSessionCount is a measure of the number of sessions currently opened. | |
| // It is EXPERIMENTAL and subject to change or removal without notice. | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry to get open_session_count metrics. | |
| OpenSessionCount = stats.Int64( | |
| statsPrefix+"open_session_count", | |
| "Number of sessions currently opened", | |
| stats.UnitDimensionless, | |
| ) | |
| // OpenSessionCountView is a view of the last value of OpenSessionCount. | |
| // It is EXPERIMENTAL and subject to change or removal without notice. | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry to get open_session_count metrics. | |
| OpenSessionCountView = &view.View{ | |
| Measure: OpenSessionCount, | |
| Aggregation: view.LastValue(), | |
| TagKeys: tagCommonKeys, | |
| } | |
| // MaxAllowedSessionsCount is a measure of the maximum number of sessions | |
| // allowed. Configurable by the user. | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry to get max_allowed_sessions metrics. | |
| MaxAllowedSessionsCount = stats.Int64( | |
| statsPrefix+"max_allowed_sessions", | |
| "The maximum number of sessions allowed. Configurable by the user.", | |
| stats.UnitDimensionless, | |
| ) | |
| // MaxAllowedSessionsCountView is a view of the last value of | |
| // MaxAllowedSessionsCount. | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry to get max_allowed_sessions metrics. | |
| MaxAllowedSessionsCountView = &view.View{ | |
| Measure: MaxAllowedSessionsCount, | |
| Aggregation: view.LastValue(), | |
| TagKeys: tagCommonKeys, | |
| } | |
| // SessionsCount is a measure of the number of sessions in the pool | |
| // including both in-use, idle, and being prepared. | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry to get num_sessions_in_pool metrics. | |
| SessionsCount = stats.Int64( | |
| statsPrefix+"num_sessions_in_pool", | |
| "The number of sessions currently in use.", | |
| stats.UnitDimensionless, | |
| ) | |
| // SessionsCountView is a view of the last value of SessionsCount. | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry to get num_sessions_in_pool metrics. | |
| SessionsCountView = &view.View{ | |
| Measure: SessionsCount, | |
| Aggregation: view.LastValue(), | |
| TagKeys: append(tagCommonKeys, tagKeyType), | |
| } | |
| // MaxInUseSessionsCount is a measure of the maximum number of sessions | |
| // in use during the last 10 minute interval. | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry to get max_in_use_sessions metrics. | |
| MaxInUseSessionsCount = stats.Int64( | |
| statsPrefix+"max_in_use_sessions", | |
| "The maximum number of sessions in use during the last 10 minute interval.", | |
| stats.UnitDimensionless, | |
| ) | |
| // MaxInUseSessionsCountView is a view of the last value of | |
| // MaxInUseSessionsCount. | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry to get max_in_use_sessions metrics. | |
| MaxInUseSessionsCountView = &view.View{ | |
| Measure: MaxInUseSessionsCount, | |
| Aggregation: view.LastValue(), | |
| TagKeys: tagCommonKeys, | |
| } | |
| // GetSessionTimeoutsCount is a measure of the number of get sessions | |
| // timeouts due to pool exhaustion. | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry to get get_session_timeouts metrics. | |
| GetSessionTimeoutsCount = stats.Int64( | |
| statsPrefix+"get_session_timeouts", | |
| "The number of get sessions timeouts due to pool exhaustion.", | |
| stats.UnitDimensionless, | |
| ) | |
| // GetSessionTimeoutsCountView is a view of the last value of | |
| // GetSessionTimeoutsCount. | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry to get get_session_timeouts metrics. | |
| GetSessionTimeoutsCountView = &view.View{ | |
| Measure: GetSessionTimeoutsCount, | |
| Aggregation: view.Count(), | |
| TagKeys: tagCommonKeys, | |
| } | |
| // AcquiredSessionsCount is the number of sessions acquired from | |
| // the session pool. | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry to get num_acquired_sessions metrics. | |
| AcquiredSessionsCount = stats.Int64( | |
| statsPrefix+"num_acquired_sessions", | |
| "The number of sessions acquired from the session pool.", | |
| stats.UnitDimensionless, | |
| ) | |
| // AcquiredSessionsCountView is a view of the last value of | |
| // AcquiredSessionsCount. | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry to get num_acquired_sessions metrics. | |
| AcquiredSessionsCountView = &view.View{ | |
| Measure: AcquiredSessionsCount, | |
| Aggregation: view.Count(), | |
| TagKeys: tagCommonKeys, | |
| } | |
| // ReleasedSessionsCount is the number of sessions released by the user | |
| // and pool maintainer. | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry to get num_released_sessions metrics. | |
| ReleasedSessionsCount = stats.Int64( | |
| statsPrefix+"num_released_sessions", | |
| "The number of sessions released by the user and pool maintainer.", | |
| stats.UnitDimensionless, | |
| ) | |
| // ReleasedSessionsCountView is a view of the last value of | |
| // ReleasedSessionsCount. | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry to get num_released_sessions metrics. | |
| ReleasedSessionsCountView = &view.View{ | |
| Measure: ReleasedSessionsCount, | |
| Aggregation: view.Count(), | |
| TagKeys: tagCommonKeys, | |
| } | |
| // GFELatency is the latency between Google's network receiving an RPC and reading back the first byte of the response | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry to get gfe_latency metrics. | |
| GFELatency = stats.Int64( | |
| statsPrefix+"gfe_latency", | |
| "Latency between Google's network receiving an RPC and reading back the first byte of the response", | |
| stats.UnitMilliseconds, | |
| ) | |
| // GFELatencyView is the view of distribution of GFELatency values | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry to get gfe_latency metrics. | |
| GFELatencyView = &view.View{ | |
| Name: "cloud.google.com/go/spanner/gfe_latency", | |
| Measure: GFELatency, | |
| Description: "Latency between Google's network receives an RPC and reads back the first byte of the response", | |
| Aggregation: view.Distribution(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), | |
| TagKeys: append(tagCommonKeys, tagKeyMethod), | |
| } | |
| // GFEHeaderMissingCount is the number of RPC responses received without the server-timing header, most likely means that the RPC never reached Google's network | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry to get gfe_header_missing_count metrics. | |
| GFEHeaderMissingCount = stats.Int64( | |
| statsPrefix+"gfe_header_missing_count", | |
| "Number of RPC responses received without the server-timing header, most likely means that the RPC never reached Google's network", | |
| stats.UnitDimensionless, | |
| ) | |
| // GFEHeaderMissingCountView is the view of number of GFEHeaderMissingCount | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry to get gfe_header_missing_count metrics. | |
| GFEHeaderMissingCountView = &view.View{ | |
| Name: "cloud.google.com/go/spanner/gfe_header_missing_count", | |
| Measure: GFEHeaderMissingCount, | |
| Description: "Number of RPC responses received without the server-timing header, most likely means that the RPC never reached Google's network", | |
| Aggregation: view.Count(), | |
| TagKeys: append(tagCommonKeys, tagKeyMethod), | |
| } | |
| ) | |
| // EnableStatViews enables all views of metrics relate to session management. | |
| // | |
| // Deprecated: OpenCensus project is deprecated. | |
| // Use EnableOpenTelemetryMetrics to get Session metrics through OpenTelemetry instrumentation. | |
| func EnableStatViews() error { | |
| return view.Register( | |
| OpenSessionCountView, | |
| MaxAllowedSessionsCountView, | |
| SessionsCountView, | |
| MaxInUseSessionsCountView, | |
| GetSessionTimeoutsCountView, | |
| AcquiredSessionsCountView, | |
| ReleasedSessionsCountView, | |
| ) | |
| } | |
| // EnableGfeLatencyView enables GFELatency metric | |
| // | |
| // Deprecated: OpenCensus project is deprecated. | |
| // Use EnableOpenTelemetryMetrics to get GfeLatency metrics through OpenTelemetry instrumentation. | |
| func EnableGfeLatencyView() error { | |
| setGFELatencyMetricsFlag(true) | |
| return view.Register(GFELatencyView) | |
| } | |
| // EnableGfeHeaderMissingCountView enables GFEHeaderMissingCount metric | |
| // | |
| // Deprecated: OpenCensus project is deprecated. | |
| // Use EnableOpenTelemetryMetrics to get GfeHeaderMissingCount metrics through OpenTelemetry instrumentation. | |
| func EnableGfeHeaderMissingCountView() error { | |
| setGFELatencyMetricsFlag(true) | |
| return view.Register(GFEHeaderMissingCountView) | |
| } | |
| // EnableGfeLatencyAndHeaderMissingCountViews enables GFEHeaderMissingCount and GFELatency metric | |
| // | |
| // Deprecated: OpenCensus project is deprecated. | |
| // Use EnableOpenTelemetryMetrics to get GfeLatency and GfeHeaderMissingCount metrics through OpenTelemetry instrumentation. | |
| func EnableGfeLatencyAndHeaderMissingCountViews() error { | |
| setGFELatencyMetricsFlag(true) | |
| return view.Register( | |
| GFELatencyView, | |
| GFEHeaderMissingCountView, | |
| ) | |
| } | |
| // Deprecated: OpenCensus project is deprecated. | |
| func getGFELatencyMetricsFlag() bool { | |
| statsMu.RLock() | |
| defer statsMu.RUnlock() | |
| return gfeLatencyMetricsEnabled | |
| } | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry for capturing metrics. | |
| func setGFELatencyMetricsFlag(enable bool) { | |
| statsMu.Lock() | |
| gfeLatencyMetricsEnabled = enable | |
| statsMu.Unlock() | |
| } | |
| // DisableGfeLatencyAndHeaderMissingCountViews disables GFEHeaderMissingCount and GFELatency metric | |
| // | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry for capturing metrics. | |
| func DisableGfeLatencyAndHeaderMissingCountViews() { | |
| setGFELatencyMetricsFlag(false) | |
| view.Unregister( | |
| GFELatencyView, | |
| GFEHeaderMissingCountView, | |
| ) | |
| } | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry for capturing metrics. | |
| func captureGFELatencyStats(ctx context.Context, md metadata.MD, keyMethod string) error { | |
| if len(md.Get("server-timing")) == 0 { | |
| recordStat(ctx, GFEHeaderMissingCount, 1) | |
| return nil | |
| } | |
| serverTiming := md.Get("server-timing")[0] | |
| gfeLatency, err := strconv.Atoi(strings.TrimPrefix(serverTiming, "gfet4t7; dur=")) | |
| if !strings.HasPrefix(serverTiming, "gfet4t7; dur=") || err != nil { | |
| return err | |
| } | |
| // Record GFE latency with OpenCensus. | |
| ctx = tag.NewContext(ctx, tag.FromContext(ctx)) | |
| ctx, err = tag.New(ctx, tag.Insert(tagKeyMethod, keyMethod)) | |
| if err != nil { | |
| return err | |
| } | |
| recordStat(ctx, GFELatency, int64(gfeLatency)) | |
| return nil | |
| } | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry for capturing metrics. | |
| func createContextAndCaptureGFELatencyMetrics(ctx context.Context, ct *commonTags, md metadata.MD, keyMethod string) error { | |
| var ctxGFE, err = tag.New(ctx, | |
| tag.Upsert(tagKeyClientID, ct.clientID), | |
| tag.Upsert(tagKeyDatabase, ct.database), | |
| tag.Upsert(tagKeyInstance, ct.instance), | |
| tag.Upsert(tagKeyLibVersion, ct.libVersion), | |
| ) | |
| if err != nil { | |
| return err | |
| } | |
| return captureGFELatencyStats(ctxGFE, md, keyMethod) | |
| } | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry for capturing metrics. | |
| func getCommonTags(sc *sessionClient) *commonTags { | |
| _, instance, database, err := parseDatabaseName(sc.database) | |
| if err != nil { | |
| return nil | |
| } | |
| return &commonTags{ | |
| clientID: sc.id, | |
| database: database, | |
| instance: instance, | |
| libVersion: internal.Version, | |
| } | |
| } | |
| // commonTags are common key-value pairs of data associated with the GFELatency measure | |
| // Deprecated: OpenCensus project is deprecated. Use OpenTelemetry for capturing metrics. | |
| type commonTags struct { | |
| // Client ID | |
| clientID string | |
| // Database Name | |
| database string | |
| // Instance ID | |
| instance string | |
| // Library Version | |
| libVersion string | |
| } |