go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/buildbucket/appengine/internal/metrics/v1.go (about) 1 // Copyright 2020 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package metrics 16 17 import ( 18 "fmt" 19 "math" 20 21 "go.chromium.org/luci/buildbucket/appengine/model" 22 pb "go.chromium.org/luci/buildbucket/proto" 23 "go.chromium.org/luci/common/tsmon/distribution" 24 "go.chromium.org/luci/common/tsmon/field" 25 "go.chromium.org/luci/common/tsmon/metric" 26 "go.chromium.org/luci/common/tsmon/types" 27 ) 28 29 var ( 30 // A common set of field definitions for build metrics. 31 fieldDefs = map[string]field.Field{ 32 "bucket": field.String("bucket"), 33 "builder": field.String("builder"), 34 "canary": field.Bool("canary"), 35 "cancelation_reason": field.String("cancelation_reason"), 36 "failure_reason": field.String("failure_reason"), 37 "must_be_never_leased": field.Bool("must_be_never_leased"), 38 "result": field.String("result"), 39 "status": field.String("status"), 40 "user_agent": field.String("user_agent"), 41 } 42 43 // V1 is a collection of metric objects for V1 metrics. 44 V1 = struct { 45 BuildCount metric.Int 46 BuildCountCreated metric.Counter 47 BuildCountStarted metric.Counter 48 BuildCountCompleted metric.Counter 49 BuildDurationCycle metric.CumulativeDistribution 50 BuildDurationRun metric.CumulativeDistribution 51 BuildDurationScheduling metric.CumulativeDistribution 52 ExpiredLeaseReset metric.Counter 53 MaxAgeScheduled metric.Float 54 }{ 55 BuildCount: metric.NewInt( 56 "buildbucket/builds/count", 57 "Number of pending/running prod builds", nil, 58 bFields("status")..., 59 ), 60 BuildCountCreated: metric.NewCounter( 61 "buildbucket/builds/created", 62 "Build creation", nil, 63 bFields("user_agent")..., 64 ), 65 BuildCountStarted: metric.NewCounter( 66 "buildbucket/builds/started", 67 "Build start", nil, 68 bFields("canary")..., 69 ), 70 BuildCountCompleted: metric.NewCounter( 71 "buildbucket/builds/completed", 72 "Build completion, including success, failure and cancellation", nil, 73 bFields("result", "failure_reason", "cancelation_reason", "canary")..., 74 ), 75 BuildDurationCycle: newbuildDurationMetric( 76 "buildbucket/builds/cycle_durations", 77 "Duration between build creation and completion", 78 ), 79 BuildDurationRun: newbuildDurationMetric( 80 "buildbucket/builds/run_durations", 81 "Duration between build start and completion", 82 ), 83 BuildDurationScheduling: newbuildDurationMetric( 84 "buildbucket/builds/scheduling_durations", 85 "Duration between build creation and start", 86 ), 87 ExpiredLeaseReset: metric.NewCounter( 88 "buildbucket/builds/lease_expired", 89 "Build lease expirations", nil, 90 bFields("status")..., 91 ), 92 MaxAgeScheduled: metric.NewFloat( 93 "buildbucket/builds/max_age_scheduled", 94 "Age of the oldest SCHEDULED build", 95 &types.MetricMetadata{Units: types.Seconds}, 96 bFields("must_be_never_leased")..., 97 ), 98 } 99 ) 100 101 func bFields(extraFields ...string) []field.Field { 102 fs := make([]field.Field, 2+len(extraFields)) 103 fs[0], fs[1] = fieldDefs["bucket"], fieldDefs["builder"] 104 for i, n := range extraFields { 105 f, ok := fieldDefs[n] 106 if !ok { 107 panic(fmt.Sprintf("unknown build field %q", n)) 108 } 109 fs[i+2] = f 110 } 111 return fs 112 } 113 114 func newbuildDurationMetric(name, description string, extraFields ...string) metric.CumulativeDistribution { 115 fs := []string{"result", "failure_reason", "cancelation_reason", "canary"} 116 return metric.NewCumulativeDistribution( 117 name, description, &types.MetricMetadata{Units: types.Seconds}, 118 // Bucketer for 1s..48h range 119 distribution.GeometricBucketer(math.Pow(10, 0.053), 100), 120 bFields(append(fs, extraFields...)...)..., 121 ) 122 } 123 124 func getLegacyMetricFields(b *model.Build) (legacyStatus, result, failureR, cancelationR string) { 125 // The default values are "" instead of UNSET for backwards compatibility. 126 switch b.Status { 127 case pb.Status_SCHEDULED: 128 legacyStatus = model.Scheduled.String() 129 case pb.Status_STARTED: 130 legacyStatus = model.Started.String() 131 case pb.Status_SUCCESS: 132 legacyStatus = model.Completed.String() 133 result = model.Success.String() 134 case pb.Status_FAILURE: 135 legacyStatus = model.Completed.String() 136 result = model.Failure.String() 137 failureR = model.BuildFailure.String() 138 case pb.Status_INFRA_FAILURE: 139 legacyStatus = model.Completed.String() 140 if b.Proto.StatusDetails.GetTimeout() != nil { 141 result = model.Canceled.String() 142 cancelationR = model.TimeoutCanceled.String() 143 } else { 144 result = model.Failure.String() 145 failureR = model.InfraFailure.String() 146 } 147 case pb.Status_CANCELED: 148 legacyStatus = model.Completed.String() 149 result = model.Canceled.String() 150 cancelationR = model.ExplicitlyCanceled.String() 151 default: 152 panic(fmt.Sprintf("getLegacyMetricFields: invalid status %q", b.Status)) 153 } 154 return 155 } 156 157 // legacyBucketName returns the V1 luci bucket name. 158 // e.g., "luci.chromium.try". 159 func legacyBucketName(project, bucket string) string { 160 return fmt.Sprintf("luci.%s.%s", project, bucket) 161 }