blob: 346652d732993ef3b6c1962440b3e597c9943516 [file] [log] [blame]
// Copyright 2020 The LUCI Authors.
//
// 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 metrics
import (
"fmt"
"math"
"go.chromium.org/luci/buildbucket/appengine/model"
pb "go.chromium.org/luci/buildbucket/proto"
"go.chromium.org/luci/common/tsmon/distribution"
"go.chromium.org/luci/common/tsmon/field"
"go.chromium.org/luci/common/tsmon/metric"
"go.chromium.org/luci/common/tsmon/types"
)
var (
// A common set of field definitions for build metrics.
fieldDefs = map[string]field.Field{
"bucket": field.String("bucket"),
"builder": field.String("builder"),
"canary": field.Bool("canary"),
"cancelation_reason": field.String("cancelation_reason"),
"failure_reason": field.String("failure_reason"),
"must_be_never_leased": field.Bool("must_be_never_leased"),
"result": field.String("result"),
"user_agent": field.String("user_agent"),
}
// V1 is a collection of metric objects for V1 metrics.
V1 = struct {
BuildCountCreated metric.Counter
BuildCountStarted metric.Counter
BuildCountCompleted metric.Counter
BuildDurationCycle metric.CumulativeDistribution
BuildDurationRun metric.CumulativeDistribution
BuildDurationScheduling metric.CumulativeDistribution
MaxAgeScheduled metric.Float
}{
BuildCountCreated: metric.NewCounter(
"buildbucket/builds/created",
"Build creation", nil,
bFields("user_agent")...,
),
BuildCountStarted: metric.NewCounter(
"buildbucket/builds/started",
"Build start", nil,
bFields("canary")...,
),
BuildCountCompleted: metric.NewCounter(
"buildbucket/builds/completed",
"Build completion, including success, failure and cancellation", nil,
bFields("result", "failure_reason", "cancelation_reason", "canary")...,
),
BuildDurationCycle: newbuildDurationMetric(
"buildbucket/builds/cycle_durations",
"Duration between build creation and completion",
),
BuildDurationRun: newbuildDurationMetric(
"buildbucket/builds/run_durations",
"Duration between build start and completion",
),
BuildDurationScheduling: newbuildDurationMetric(
"buildbucket/builds/scheduling_durations",
"Duration between build creation and start",
),
MaxAgeScheduled: metric.NewFloat(
"buildbucket/builds/max_age_scheduled",
"Age of the oldest SCHEDULED build",
&types.MetricMetadata{Units: types.Seconds},
bFields("must_be_never_leased")...,
),
}
)
func bFields(extraFields ...string) []field.Field {
fs := make([]field.Field, 2+len(extraFields))
fs[0], fs[1] = fieldDefs["bucket"], fieldDefs["builder"]
for i, n := range extraFields {
f, ok := fieldDefs[n]
if !ok {
panic(fmt.Sprintf("unknown build field %q", n))
}
fs[i+2] = f
}
return fs
}
func newbuildDurationMetric(name, description string, extraFields ...string) metric.CumulativeDistribution {
fs := []string{"result", "failure_reason", "cancelation_reason", "canary"}
return metric.NewCumulativeDistribution(
name, description, &types.MetricMetadata{Units: types.Seconds},
// Bucketer for 1s..48h range
distribution.GeometricBucketer(math.Pow(10, 0.053), 100),
bFields(append(fs, extraFields...)...)...,
)
}
func getLegacyMetricFields(b *model.Build) (result, failureR, cancelationR string) {
// The default values are "" instead of UNSET for backwards compatibility.
switch b.Status {
case pb.Status_SCHEDULED:
case pb.Status_STARTED:
case pb.Status_SUCCESS:
result = model.Success.String()
case pb.Status_FAILURE:
result = model.Failure.String()
failureR = model.BuildFailure.String()
case pb.Status_INFRA_FAILURE:
if b.Proto.StatusDetails.GetTimeout() != nil {
result = model.Canceled.String()
cancelationR = model.TimeoutCanceled.String()
} else {
result = model.Failure.String()
failureR = model.InfraFailure.String()
}
case pb.Status_CANCELED:
result = model.Canceled.String()
cancelationR = model.ExplicitlyCanceled.String()
default:
panic(fmt.Sprintf("getLegacyMetricFields: invalid status %q", b.Status))
}
return
}
// legacyBucketName returns the V1 luci bucket name.
// e.g., "luci.chromium.try".
func legacyBucketName(project, bucket string) string {
return fmt.Sprintf("luci.%s.%s", project, bucket)
}