blob: b140974a1bd9407dcd79f1fe885000a835be581c [file] [log] [blame]
// Copyright 2019 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.
// dummy_project implements a monitoring target interface for DummyProject.
package dummy_project
import (
"fmt"
"hash/fnv"
"reflect"
"github.com/golang/protobuf/proto"
"go.chromium.org/luci/common/tsmon/target"
pb "go.chromium.org/luci/common/tsmon/ts_mon_proto"
"go.chromium.org/luci/common/tsmon/types"
)
// Hash returns a uint64 hash of this target.
func (t *DummyProject) Hash() uint64 {
h := fnv.New64a()
h.Write([]byte(fmt.Sprintf("%+v", t)))
return h.Sum64()
}
// Type returns the TargetType of DummyProject.
func (t *DummyProject) Type() types.TargetType {
pname := proto.MessageName((*DummyProject)(t))
if pname == "" {
panic("a unregistered proto target.")
}
return types.TargetType{
Name: proto.MessageName((*DummyProject)(t)),
Type: reflect.TypeOf(t),
}
}
// Clone returns a copy of this object.
func (t *DummyProject) Clone() types.Target {
clone := *t
return &clone
}
// PopulateProto implements Target.
func (t *DummyProject) PopulateProto(d *pb.MetricsCollection) {
// * Note for the implementation choice of PopulateProto.
//
// types.Target interface requires each Target to implement PopulateProto,
// and the role of the function is to convert to a given Target instance
// into MetricsCollection, which is the message format that the tsmon
// backend supports.
//
// There are two ways of implementing the function, and each has pros and
// cons.
//
// (1) Manually list all the target fields.
// Pros
// - faster than (2)
// - the code can look more intuitive, as the mapping between the field
// name in the monitoring data and the struct field can be easily found.
//
// Cons
// - need to update the list every time the target proto changes.
// However, please be aware that target proto should be changed carefully.
// Modifying a target proto requires updating the proto in the monitoring
// backend, and all the changes must be backward-compatible.
//
d.RootLabels = append(
d.RootLabels,
target.RootLabel("project", t.Project),
target.RootLabel("location", t.Location),
target.RootLabel("is_staging", t.IsStaging),
)
// (2) Iterate the proto struct fields and generate RootLabels.
// Pros
// - No need to list all the target fields manually.
// Cons
// - Slower than (1), as it parses the tag of each field and appends the
// RootLabel into MetricsCollection iteratively. However, it's questionable
// whether (2) is meaningfully slower than (1).
// Target.PopulateProto is invoked once for each Target instance every time
// tsmon.Flush is invoked. Typically, an application handles a small number
// of monitoring targets. However, platform software tends to report
// monitoring data for a large number of different monitoring targets, each
// represents a client of the platform. If a given Target proto is used to
// report report monitoring data in platform software, (1) would probably
// be a better choice, but, the choice of (1) and (2) won't make a much
// difference, otherwise, because # of monitoring targets is as small as
// < 10.
// t.toMetricsProto(d) is an implementation of (2). It can be used to
// convert any proto-based Target instance to MetricsCollection.
//
// t.toMetricsProto(d)
}
func (t *DummyProject) toMetricsProto(d *pb.MetricsCollection) {
st := t.Type().Type.Elem()
sv := reflect.Indirect(reflect.ValueOf(t))
for i := 0; i < st.NumField(); i++ {
props := new(proto.Properties)
tag, ok := st.Field(i).Tag.Lookup("protobuf")
if !ok {
continue
}
props.Parse(tag)
var v any
switch fv := sv.Field(i); fv.Kind() {
case reflect.Int64:
v = fv.Int()
case reflect.String:
v = fv.String()
case reflect.Bool:
v = fv.Bool()
default:
panic(fmt.Sprintf("Unsupported target-field type %q", fv.Kind()))
}
d.RootLabels = append(
d.RootLabels, target.RootLabel(props.OrigName, v))
}
}