Skip to content

Commit f4d2d10

Browse files
committed
feature: count json objects with same value
if we want to count json objects with same values we can use countbylabel type in the metric configuration Signed-off-by: Parsa <[email protected]>
1 parent 7ab7efc commit f4d2d10

File tree

7 files changed

+89
-2
lines changed

7 files changed

+89
-2
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ Serving HTTP on :: port 8000 (http://[::]:8000/) ...
2121
## TEST with 'default' module
2222

2323
$ curl "http://localhost:7979/probe?module=default&target=http://localhost:8000/examples/data.json"
24+
# HELP example_count Example of count object from a json
25+
# TYPE example_count untyped
26+
example_count{environment="beta",state="ACTIVE"} 2
27+
example_count{environment="beta",state="INACTIVE"} 1
2428
# HELP example_global_value Example of a top-level global value scrape in the json
2529
# TYPE example_global_value untyped
2630
example_global_value{environment="beta",location="planet-mars"} 1234

config/config.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,15 @@ type Metric struct {
3030
EpochTimestamp string
3131
Help string
3232
Values map[string]string
33+
MinimumCount int
3334
}
3435

3536
type ScrapeType string
3637

3738
const (
3839
ValueScrape ScrapeType = "value" // default
3940
ObjectScrape ScrapeType = "object"
41+
CountScrape ScrapeType = "countbylabel"
4042
)
4143

4244
type ValueType string
@@ -89,8 +91,10 @@ func LoadConfig(configPath string) (Config, error) {
8991
if module.Metrics[i].ValueType == "" {
9092
module.Metrics[i].ValueType = ValueTypeUntyped
9193
}
94+
if !(module.Metrics[i].MinimumCount > 0) {
95+
module.Metrics[i].MinimumCount = 1
96+
}
9297
}
9398
}
94-
9599
return config, nil
96100
}

examples/config.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ modules:
3030
active: 1 # static value
3131
count: '{.count}' # dynamic value
3232
boolean: '{.some_boolean}'
33+
- name: example_count
34+
type: countbylabel
35+
help: Example of count json labels
36+
path: '{.values[*].state}'
37+
labels:
38+
environment: beta # static label
39+
state: '{}' # dynamic label
40+
# The minimum count of labels required to expose the metric for each label. Defaults to 1.
41+
minimumCount: 1
3342

3443
animals:
3544
metrics:
@@ -66,4 +75,3 @@ modules:
6675
# content: |
6776
# {"time_diff": "{{ duration `95` }}","anotherVar": "{{ .myVal | first }}"}
6877
# templatize: true
69-

exporter/collector.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type JSONMetric struct {
3939
LabelsJSONPaths []string
4040
ValueType prometheus.ValueType
4141
EpochTimestampJSONPath string
42+
MinimumCount int
4243
}
4344

4445
func (mc JSONMetricCollector) Describe(ch chan<- *prometheus.Desc) {
@@ -70,6 +71,42 @@ func (mc JSONMetricCollector) Collect(ch chan<- prometheus.Metric) {
7071
continue
7172
}
7273

74+
case config.CountScrape:
75+
values, err := extractValue(mc.Logger, mc.Data, m.KeyJSONPath, true)
76+
if err != nil {
77+
level.Error(mc.Logger).Log("msg", "Failed to extract json objects for metric", "err", err, "metric", m.Desc)
78+
continue
79+
}
80+
81+
var jsonData []interface{}
82+
counts := make(map[interface{}]int)
83+
84+
if err := json.Unmarshal([]byte(values), &jsonData); err == nil {
85+
for _, data := range jsonData {
86+
counts[data]++
87+
}
88+
for data, count := range counts {
89+
if count >= m.MinimumCount {
90+
jdata, err := json.Marshal(data)
91+
if err != nil {
92+
level.Error(mc.Logger).Log("msg", "Failed to marshal data to json", "path", m.ValueJSONPath, "err", err, "metric", m.Desc, "data", data)
93+
continue
94+
}
95+
if err != nil {
96+
level.Error(mc.Logger).Log("msg", "Failed to extract value for metric", "path", m.ValueJSONPath, "err", err, "metric", m.Desc)
97+
continue
98+
}
99+
100+
ch <- prometheus.MustNewConstMetric(
101+
m.Desc,
102+
prometheus.UntypedValue,
103+
float64(count),
104+
extractLabels(mc.Logger, jdata, m.LabelsJSONPaths)...,
105+
)
106+
}
107+
}
108+
}
109+
73110
case config.ObjectScrape:
74111
values, err := extractValue(mc.Logger, mc.Data, m.KeyJSONPath, true)
75112
if err != nil {

exporter/util.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,28 @@ func CreateMetricsList(c config.Module) ([]JSONMetric, error) {
133133
}
134134
metrics = append(metrics, jsonMetric)
135135
}
136+
case config.CountScrape:
137+
var variableLabels, variableLabelsValues []string
138+
for k, v := range metric.Labels {
139+
variableLabels = append(variableLabels, k)
140+
variableLabelsValues = append(variableLabelsValues, v)
141+
}
142+
jsonMetric := JSONMetric{
143+
Type: config.CountScrape,
144+
Desc: prometheus.NewDesc(
145+
metric.Name,
146+
metric.Help,
147+
variableLabels,
148+
nil,
149+
),
150+
KeyJSONPath: metric.Path,
151+
MinimumCount: metric.MinimumCount,
152+
LabelsJSONPaths: variableLabelsValues,
153+
ValueType: valueType,
154+
EpochTimestampJSONPath: metric.EpochTimestamp,
155+
}
156+
fmt.Println(jsonMetric)
157+
metrics = append(metrics, jsonMetric)
136158
default:
137159
return nil, fmt.Errorf("Unknown metric type: '%s', for metric: '%s'", metric.Type, metric.Name)
138160
}

test/config/good.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,11 @@ modules:
2222
active: 1 # static value
2323
count: '{.count}' # dynamic value
2424
boolean: '{.some_boolean}'
25+
26+
- name: example_count
27+
type: countbylabel
28+
help: Example of count object from a json
29+
path: '{.values[*].state}'
30+
labels:
31+
environment: beta # static label
32+
state: '{}' # dynamic label

test/response/good.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# HELP example_count Example of count object from a json
2+
# TYPE example_count untyped
3+
example_count{environment="beta",state="ACTIVE"} 2
4+
example_count{environment="beta",state="INACTIVE"} 1
15
# HELP example_global_value Example of a top-level global value scrape in the json
26
# TYPE example_global_value gauge
37
example_global_value{environment="beta",location="planet-mars"} 1234

0 commit comments

Comments
 (0)