/
metricconfig.go
238 lines (225 loc) · 8.54 KB
/
metricconfig.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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
package conviva
import (
"encoding/json"
"strconv"
"strings"
"sync"
)
var metricLensMetrics = map[string][]string{
"quality_metriclens": {
"conviva.quality_metriclens.total_attempts",
"conviva.quality_metriclens.video_start_failures_percent",
"conviva.quality_metriclens.exits_before_video_start_percent",
"conviva.quality_metriclens.plays_percent",
"conviva.quality_metriclens.video_startup_time_sec",
"conviva.quality_metriclens.rebuffering_ratio_percent",
"conviva.quality_metriclens.average_bitrate_kbps",
"conviva.quality_metriclens.video_playback_failures_percent",
"conviva.quality_metriclens.ended_plays",
"conviva.quality_metriclens.connection_induced_rebuffering_ratio_percent",
"conviva.quality_metriclens.video_restart_time",
},
"audience_metriclens": {
"conviva.audience_metriclens.concurrent_plays",
"conviva.audience_metriclens.plays",
"conviva.audience_metriclens.ended_plays",
},
}
// metricConfig for configuring individual metric
type metricConfig struct {
// Conviva customer account name. The default account is fetched used if not specified.
Account string `yaml:"account"`
MetricParameter string `yaml:"metricParameter" default:"quality_metriclens"`
// Filter names. The default is `All Traffic` filter
Filters []string `yaml:"filters"`
// MetricLens dimension names. The default is names of all MetricLens dimensions of the account
MetricLensDimensions []string `yaml:"metricLensDimensions"`
// MetricLens dimension names to exclude.
ExcludeMetricLensDimensions []string `yaml:"excludeMetricLensDimensions"`
accountID string
// id:name map of filters derived from the configured Filters
filtersMap map[string]string
// name:id map of MetricLens dimensions derived from configured MetricLensDimensions
metricLensDimensionMap map[string]float64
isInitialized bool
// id:name map of filters in filters_warmup status on response
filtersWarmup map[string]string
// id:name map of filters in filters_not_exist status on response
filtersNotExist map[string]string
// id:name map of filters in filters_incomplete_data status on response
filtersIncompleteData map[string]string
mutex sync.RWMutex
}
func (mc *metricConfig) filterName(filterID string) string {
if len(mc.filtersMap) != 0 {
return mc.filtersMap[filterID]
}
return ""
}
func (mc *metricConfig) init(service accountsService) {
mc.mutex.Lock()
defer mc.mutex.Unlock()
if !mc.isInitialized {
if err := mc.setAccount(service); err != nil {
logger.Errorf("Metric %s account setting failure. %+v", mc.MetricParameter, err)
return
}
if err := mc.setFilters(service); err != nil {
logger.Errorf("Metric %s filter(s) setting failure. %+v", mc.MetricParameter, err)
return
}
if err := mc.setMetricLensDimensions(service); err != nil {
logger.Errorf("Metric %s MetricLens dimension(s) setting failure. %+v", mc.MetricParameter, err)
return
}
if err := mc.excludeMetricLensDimensions(service); err != nil {
logger.Errorf("Metric %s MetricLens dimension(s) exclusion failure. %+v", mc.MetricParameter, err)
return
}
mc.isInitialized = true
}
}
// setting account id and default account if necessary
func (mc *metricConfig) setAccount(service accountsService) error {
mc.Account = strings.TrimSpace(mc.Account)
if mc.Account == "" {
if defaultAccount, err := service.getDefault(); err == nil {
mc.Account = defaultAccount.Name
} else {
return err
}
}
var err error
if mc.accountID, err = service.getID(mc.Account); err != nil {
return err
}
return nil
}
func (mc *metricConfig) setFilters(service accountsService) error {
if len(mc.Filters) == 0 {
mc.Filters = []string{"All Traffic"}
if id, err := service.getFilterID(mc.Account, "All Traffic"); err == nil {
mc.filtersMap = map[string]string{id: "All Traffic"}
} else {
return err
}
} else if strings.TrimSpace(mc.Filters[0]) == "_ALL_" {
var allFilters map[string]string
var err error
if strings.Contains(mc.MetricParameter, "metriclens") {
if allFilters, err = service.getMetricLensFilters(mc.Account); err != nil {
return err
}
} else {
if allFilters, err = service.getFilters(mc.Account); err != nil {
return err
}
}
mc.Filters = make([]string, 0, len(allFilters))
mc.filtersMap = make(map[string]string, len(allFilters))
for id, name := range allFilters {
mc.Filters = append(mc.Filters, name)
mc.filtersMap[id] = name
}
} else {
mc.filtersMap = make(map[string]string, len(mc.Filters))
for _, name := range mc.Filters {
name = strings.TrimSpace(name)
if id, err := service.getFilterID(mc.Account, name); err == nil {
mc.filtersMap[id] = name
} else {
return err
}
}
}
return nil
}
func (mc *metricConfig) setMetricLensDimensions(service accountsService) error {
if strings.Contains(mc.MetricParameter, "metriclens") {
if len(mc.MetricLensDimensions) == 0 || strings.TrimSpace(mc.MetricLensDimensions[0]) == "_ALL_" {
if metricLensDimensionMap, err := service.getMetricLensDimensionMap(mc.Account); err == nil {
mc.MetricLensDimensions = make([]string, 0, len(metricLensDimensionMap))
mc.metricLensDimensionMap = make(map[string]float64, len(metricLensDimensionMap))
for name, id := range metricLensDimensionMap {
mc.MetricLensDimensions = append(mc.MetricLensDimensions, name)
mc.metricLensDimensionMap[name] = id
}
} else {
return err
}
} else {
mc.metricLensDimensionMap = make(map[string]float64, len(mc.MetricLensDimensions))
for i, name := range mc.MetricLensDimensions {
name := strings.TrimSpace(name)
if id, err := service.getMetricLensDimensionID(mc.Account, name); err == nil {
mc.MetricLensDimensions[i] = name
mc.metricLensDimensionMap[name] = id
} else {
return err
}
}
}
}
return nil
}
func (mc *metricConfig) excludeMetricLensDimensions(service accountsService) error {
for _, excludeName := range mc.ExcludeMetricLensDimensions {
excludeName := strings.TrimSpace(excludeName)
if _, err := service.getMetricLensDimensionID(mc.Account, excludeName); err == nil {
delete(mc.metricLensDimensionMap, excludeName)
} else {
return err
}
}
if len(mc.metricLensDimensionMap) < len(mc.MetricLensDimensions) {
mc.MetricLensDimensions = make([]string, 0, len(mc.metricLensDimensionMap))
for name := range mc.metricLensDimensionMap {
mc.MetricLensDimensions = append(mc.MetricLensDimensions, name)
}
}
return nil
}
func (mc *metricConfig) filterIDs() []string {
ids := make([]string, 0, len(mc.filtersMap))
for id := range mc.filtersMap {
ids = append(ids, id)
}
return ids
}
// logs filter status only when the filter status changes
func (mc *metricConfig) logFilterStatuses(filtersWarmupIds []int64, filtersNotExistIds []int64, filtersIncompleteDataIds []int64) {
mc.filtersWarmup = logFilterStatusesHelper(mc.MetricParameter, mc.filtersMap, mc.filtersWarmup, filtersWarmupIds, "filters_warmup")
mc.filtersNotExist = logFilterStatusesHelper(mc.MetricParameter, mc.filtersMap, mc.filtersNotExist, filtersNotExistIds, "filters_not_exist")
mc.filtersIncompleteData = logFilterStatusesHelper(mc.MetricParameter, mc.filtersMap, mc.filtersIncompleteData, filtersIncompleteDataIds, "filters_incomplete_data")
}
func logFilterStatusesHelper(metric string, filters map[string]string, filterStatusesCurrent map[string]string, filterStatusesIDsNew []int64, status string) map[string]string {
filterStatusesNew := map[string]string{}
filterStatusesToLog := map[string]string{}
if filterStatusesCurrent == nil {
filterStatusesCurrent = map[string]string{}
}
for _, id := range filterStatusesIDsNew {
id := strconv.FormatInt(id, 10)
if filterStatusesCurrent[id] == "" {
filterStatusesToLog[id] = filters[id]
} else {
delete(filterStatusesCurrent, id)
}
filterStatusesNew[id] = filters[id]
}
if len(filterStatusesToLog) != 0 {
if m, err := json.Marshal(filterStatusesToLog); err == nil {
logger.Warnf("GET metric %s has filters in the unresponsive %s status. Set log level to debug to see status change to responsive in future requests. Filters in %s status: %s", metric, status, status, m)
} else {
logger.Errorf("Failed marshalling id:name map of filters in %s status. %+v", status, err)
}
}
if len(filterStatusesCurrent) != 0 {
if m, err := json.Marshal(filterStatusesCurrent); err == nil {
logger.Debugf("GET metric %s has filters whose status changed from %s to responsive. Filters whose status changed: %s", metric, status, m)
} else {
logger.Errorf("Failed marshalling id:name map of filters out of %s status. %+v", status, err)
}
}
return filterStatusesNew
}