/
dummy_project.go
135 lines (122 loc) · 4.37 KB
/
dummy_project.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// 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 interface{}
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))
}
}