Skip to content

Commit 1fbf082

Browse files
author
archer-v
committed
rework custom metric labels
1 parent 602212d commit 1fbf082

File tree

7 files changed

+178
-131
lines changed

7 files changed

+178
-131
lines changed

src/configuration/script.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ type script struct {
1515
}
1616

1717
type task struct {
18-
Name string
19-
Probe probe
20-
CGroup string
21-
MetricLabels model.MetricLabels `json:"metricLabels"`
18+
Name string
19+
Probe probe
20+
CGroup string
21+
Metric model.TaskMetric `json:"metric"`
2222
}
2323

2424
type probe struct {
@@ -77,7 +77,7 @@ func ScriptYMLConfiguration(data []byte, overrideConfigOptions ...map[string]map
7777
err = fmt.Errorf("[%v] %w", t.Name, err)
7878
return
7979
}
80-
s.AddTask(model.NewTask(t.Name, t.CGroup, t.MetricLabels, p))
80+
s.AddTask(model.NewTask(t.Name, t.CGroup, t.Metric, p))
8181
}
8282
return
8383
}

src/model/task.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ package model
33
import "context"
44

55
type Task struct {
6-
Name string
7-
CGroup string `json:"-"`
8-
MetricLabels MetricLabels `json:"-"`
9-
Probe Prober
6+
Name string
7+
CGroup string `json:"-"`
8+
Metric TaskMetric `json:"-"`
9+
Probe Prober
1010
Worker
1111
}
1212

@@ -47,12 +47,12 @@ func (t *Task) ResultFinished() (tr TaskResult) {
4747
return
4848
}
4949

50-
func NewTask(taskName string, cgroup string, metricLabels MetricLabels, probe Prober) (t *Task) {
50+
func NewTask(taskName string, cgroup string, metric TaskMetric, probe Prober) (t *Task) {
5151
t = &Task{
52-
Name: taskName,
53-
CGroup: cgroup,
54-
MetricLabels: metricLabels,
55-
Probe: probe,
52+
Name: taskName,
53+
CGroup: cgroup,
54+
Metric: metric,
55+
Probe: probe,
5656
Worker: Worker{
5757
EStatus: EStatusNew,
5858
},

src/model/metricLabels.go src/model/taskMetric.go

+7-14
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,20 @@ package model
22

33
import (
44
"encoding/json"
5-
"sort"
6-
"strings"
75
)
86

7+
type TaskMetric struct {
8+
Labels MetricLabels
9+
ValueMap MetricLabelsValueMap
10+
}
11+
912
type MetricLabels struct {
1013
data map[string]string
1114
key string
1215
}
1316

17+
type MetricLabelsValueMap map[string]string
18+
1419
func (s *MetricLabels) UnmarshalJSON(b []byte) (err error) {
1520
data := MetricLabels{data: map[string]string{}}
1621

@@ -24,18 +29,6 @@ func (s *MetricLabels) UnmarshalJSON(b []byte) (err error) {
2429
return
2530
}
2631

27-
func (s *MetricLabels) CombinedKey() string {
28-
if s.key == "" && len(s.data) > 0 {
29-
keys := make([]string, 0, len(s.data))
30-
for k := range s.data {
31-
keys = append(keys, k)
32-
sort.Strings(keys)
33-
}
34-
s.key = strings.Join(keys, "|")
35-
}
36-
return s.key
37-
}
38-
3932
func (s *MetricLabels) Data() map[string]string {
4033
return s.data
4134
}

src/services/scheduler/prometheus.go

+131-80
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,60 @@ import (
99
)
1010

1111
var (
12-
probeDataGeneralLabels = []string{"job", "script", "task", "probe"}
13-
probeDateItemGeneralLabels = []string{"job", "script", "task", "probe", "item"}
14-
taskGeneralLabels = []string{"job", "script", "task"}
12+
LabelsScriptGeneral = []string{"job", "script"}
13+
LabelsTaskGeneral = []string{"job", "script", "task"}
14+
LabelsProbeDataGeneral = []string{"job", "script", "task", "probe"}
15+
LabelsProbeDateItemGeneral = []string{"job", "script", "task", "probe", "item"}
1516
)
1617

1718
const (
18-
probeDataHelpDescr = "probe execution data result"
19-
probeDataName = "boogieman_probe_data"
20-
probeDataItemName = "boogieman_probe_data_item"
21-
descriptorKeyProbeData = "probe_data"
22-
descriptorKeyProbeDataItem = "probe_data_item"
19+
probeDataHelpDescr = "probe execution data result"
20+
pNameData = "boogieman_probe_data"
21+
pNameDataItem = "boogieman_probe_data_item"
22+
pNameScriptResult = "boogieman_script_result"
23+
pNameTaskResult = "boogieman_task_result"
24+
pNameTaskRuntime = "boogieman_task_runtime"
25+
pNameTaskRuns = "boogieman_task_runs"
2326
)
2427

25-
// prometheus metrics descriptors
26-
var pDescriptors = map[string]*prometheus.Desc{
27-
"script_result": prometheus.NewDesc("boogieman_script_result", "script execution result", []string{"job", "script"}, nil),
28-
"task_result": prometheus.NewDesc("boogieman_task_result", "task execution result", taskGeneralLabels, nil),
29-
"task_runtime": prometheus.NewDesc("boogieman_task_runtime", "task runtime", taskGeneralLabels, nil),
30-
"task_runs": prometheus.NewDesc("boogieman_task_runs", "task run counter", taskGeneralLabels, nil),
31-
descriptorKeyProbeData: prometheus.NewDesc(probeDataName, probeDataHelpDescr, probeDataGeneralLabels, nil),
32-
descriptorKeyProbeDataItem: prometheus.NewDesc(probeDataItemName, probeDataHelpDescr, probeDateItemGeneralLabels, nil),
28+
// probe metrics struct
29+
type metricData struct {
30+
valueType prometheus.ValueType
31+
value float64
32+
labels []string
33+
constLabels prometheus.Labels
3334
}
3435

35-
// probe metrics struct
36-
type probeDataMetric struct {
37-
valueType prometheus.ValueType
38-
value float64
39-
labels []string
36+
type metricDescriptorInfo struct {
37+
name string
38+
help string
39+
labels []string
40+
}
41+
42+
var metricBaseDescriptors = map[string]metricDescriptorInfo{
43+
pNameScriptResult: {
44+
pNameScriptResult, "script execution result", LabelsScriptGeneral,
45+
},
46+
pNameTaskResult: {
47+
pNameTaskResult, "task execution result", LabelsTaskGeneral,
48+
},
49+
pNameTaskRuntime: {
50+
pNameTaskRuntime, "task runtime", LabelsTaskGeneral,
51+
},
52+
pNameTaskRuns: {
53+
pNameTaskRuns, "task run counter", LabelsTaskGeneral,
54+
},
55+
pNameData: {
56+
pNameData, probeDataHelpDescr, LabelsProbeDataGeneral,
57+
},
58+
pNameDataItem: {
59+
pNameDataItem, probeDataHelpDescr, LabelsProbeDateItemGeneral,
60+
},
4061
}
4162

63+
// prometheus metrics descriptors
64+
var pDescriptors = map[string]*prometheus.Desc{}
65+
4266
// Describe - implementation of prometheus.Collector interface
4367
func (s *Scheduler) Describe(chan<- *prometheus.Desc) {
4468

@@ -51,82 +75,65 @@ func (s *Scheduler) Collect(ch chan<- prometheus.Metric) {
5175
defer s.Unlock()
5276
for _, j := range s.jobs {
5377
scriptResult := j.Script.ResultFinished()
54-
ch <- prometheus.MustNewConstMetric(
55-
pDescriptors["script_result"],
56-
prometheus.GaugeValue, gbValue(scriptResult.Success),
57-
j.Name, j.ScriptFile,
78+
s.sendMetric(ch, []string{pNameScriptResult},
79+
metricData{prometheus.GaugeValue, gbValue(scriptResult.Success), []string{j.Name, j.ScriptFile}, nil},
5880
)
5981
for _, t := range scriptResult.Tasks {
6082
// task general metrics
6183
taskMetricLabelValues := []string{j.Name, j.ScriptFile, t.Name}
62-
ch <- prometheus.MustNewConstMetric(
63-
pDescriptors["task_result"],
64-
prometheus.GaugeValue, gbValue(t.Success),
65-
taskMetricLabelValues...,
66-
)
67-
ch <- prometheus.MustNewConstMetric(
68-
pDescriptors["task_runtime"],
69-
prometheus.GaugeValue, float64(t.RuntimeMs),
70-
taskMetricLabelValues...,
71-
)
72-
ch <- prometheus.MustNewConstMetric(
73-
pDescriptors["task_runs"],
74-
prometheus.CounterValue, float64(t.RunCounter),
75-
taskMetricLabelValues...,
76-
)
77-
// task data metrics
78-
if t.Probe.Data != nil {
79-
// check if there are additional metric labels for this task
80-
var taskLabels model.MetricLabels
81-
for _, task := range j.Script.Tasks {
82-
if task.Name == t.Name {
83-
taskLabels = task.MetricLabels
84-
break
85-
}
86-
}
87-
if !taskLabels.IsEmpty() {
8884

85+
// check if there are additional metric labels for this task
86+
var taskMetric model.TaskMetric
87+
for _, task := range j.Script.Tasks {
88+
if task.Name == t.Name {
89+
taskMetric = task.Metric
90+
break
8991
}
92+
}
93+
s.sendMetric(
94+
ch, []string{pNameTaskResult},
95+
metricData{prometheus.GaugeValue, gbValue(t.Success), taskMetricLabelValues, taskMetric.Labels.Data()})
96+
s.sendMetric(
97+
ch, []string{pNameTaskRuntime},
98+
metricData{prometheus.GaugeValue, float64(t.RuntimeMs), taskMetricLabelValues, taskMetric.Labels.Data()})
99+
s.sendMetric(
100+
ch, []string{pNameTaskRuns},
101+
metricData{prometheus.CounterValue, float64(t.RunCounter), taskMetricLabelValues, taskMetric.Labels.Data()})
102+
103+
// task data metrics
104+
if t.Probe.Data != nil {
105+
// add probe name to metric labels
90106
dataMetricLabelValues := taskMetricLabelValues[:]
91107
dataMetricLabelValues = append(dataMetricLabelValues, t.Probe.Name)
92108
ms := probeMetrics(t.Probe.Data)
93109
for _, m := range ms {
94110
var (
95111
labelValues []string
96-
pDescriptorKey string
112+
pDescriptorKey []string
97113
)
98114
if len(m.labels) == 0 {
99115
labelValues = dataMetricLabelValues
100-
pDescriptorKey = descriptorKeyProbeData
116+
pDescriptorKey = []string{pNameData}
101117
} else {
102118
labelValues = dataMetricLabelValues[:]
103-
labelValues = append(labelValues, m.labels...)
104-
pDescriptorKey = descriptorKeyProbeDataItem
105-
}
106-
// has dynamic labels
107-
if !taskLabels.IsEmpty() {
108-
descriptorKey := strings.Join(taskMetricLabelValues, "|")
109-
if _, exists := pDescriptors[descriptorKey]; !exists {
110-
var (
111-
labels []string
112-
name string
113-
)
114-
115-
switch pDescriptorKey {
116-
case descriptorKeyProbeData:
117-
labels = probeDataGeneralLabels
118-
name = probeDataName
119-
case descriptorKeyProbeDataItem:
120-
labels = probeDateItemGeneralLabels
121-
name = probeDataItemName
119+
if len(taskMetric.ValueMap) > 0 {
120+
val, ok := taskMetric.ValueMap[m.labels[0]]
121+
if ok {
122+
m.labels[0] = val
122123
}
123-
124-
pDescriptors[descriptorKey] =
125-
prometheus.NewDesc(name, probeDataHelpDescr, labels, taskLabels.Data())
126124
}
127-
pDescriptorKey = descriptorKey
125+
labelValues = append(labelValues, m.labels...)
126+
pDescriptorKey = []string{pNameDataItem}
127+
}
128+
// if task has custom labels
129+
if !taskMetric.Labels.IsEmpty() {
130+
pDescriptorKey = append(pDescriptorKey, taskMetricLabelValues...)
128131
}
129-
ch <- prometheus.MustNewConstMetric(pDescriptors[pDescriptorKey], m.valueType, m.value, labelValues...)
132+
s.sendMetric(
133+
ch, pDescriptorKey,
134+
metricData{
135+
valueType: m.valueType, value: m.value, labels: labelValues, constLabels: taskMetric.Labels.Data(),
136+
})
130137
}
131138
}
132139
}
@@ -141,6 +148,50 @@ func gbValue(v bool) float64 {
141148
return 0
142149
}
143150

151+
func (s *Scheduler) sendMetric(ch chan<- prometheus.Metric, descrCompositeKey []string, metricData metricData) {
152+
153+
var (
154+
descrKey string
155+
pDescr *prometheus.Desc
156+
ok bool
157+
)
158+
159+
// metric descriptor key is a concatenation of base metric descriptor key and task labels
160+
if len(descrCompositeKey) == 1 && len(metricData.constLabels) > 0 {
161+
descrCompositeKey = append(descrCompositeKey, metricData.labels...)
162+
}
163+
164+
if len(descrCompositeKey) == 1 {
165+
descrKey = descrCompositeKey[0]
166+
} else {
167+
descrKey = strings.Join(descrCompositeKey, "|")
168+
}
169+
170+
// check if metric descriptor already exists
171+
pDescr, ok = pDescriptors[descrKey]
172+
if !ok { // metric descriptor not found, create it
173+
descrInfo, ok := metricBaseDescriptors[descrKey]
174+
if ok {
175+
pDescr = prometheus.NewDesc(descrKey, descrInfo.help, descrInfo.labels, metricData.constLabels)
176+
pDescriptors[descrKey] = pDescr
177+
} else {
178+
descrInfo, ok = metricBaseDescriptors[descrCompositeKey[0]]
179+
if !ok {
180+
s.logger.Printf("unknown base metric descriptor: %s", descrCompositeKey[0])
181+
return
182+
}
183+
pDescr =
184+
prometheus.NewDesc(descrCompositeKey[0], descrInfo.help, descrInfo.labels, metricData.constLabels)
185+
pDescriptors[descrKey] = pDescr
186+
}
187+
}
188+
ch <- prometheus.MustNewConstMetric(
189+
pDescr,
190+
metricData.valueType, metricData.value,
191+
metricData.labels...,
192+
)
193+
}
194+
144195
func addToArray(arr []string, s string) []string {
145196
r := make([]string, len(arr)+1)
146197
copy(r, arr)
@@ -160,7 +211,7 @@ func reflectIsFloat(kind reflect.Kind) bool {
160211
// data can be a simple value or hash of names and values
161212
//
162213
//nolint:funlen
163-
func probeMetrics(data any) (metrics []probeDataMetric) {
214+
func probeMetrics(data any) (metrics []metricData) {
164215
var (
165216
valueType = prometheus.GaugeValue
166217
labels []string
@@ -203,7 +254,7 @@ func probeMetrics(data any) (metrics []probeDataMetric) {
203254
continue
204255
}
205256

206-
metrics = append(metrics, probeDataMetric{
257+
metrics = append(metrics, metricData{
207258
valueType: valueType,
208259
value: value,
209260
labels: labels,
@@ -225,7 +276,7 @@ func probeMetrics(data any) (metrics []probeDataMetric) {
225276
labels = []string{v.String()}
226277
}
227278

228-
return []probeDataMetric{
279+
return []metricData{
229280
{
230281
valueType: valueType,
231282
value: value,

0 commit comments

Comments
 (0)