Skip to content

Commit cedb5a5

Browse files
ngrebelsdependabot[bot]yhk-mw
authored andcommitted
Support Value Conversions (prometheus-community#172)
* Bump k8s.io/client-go from 0.24.2 to 0.24.3 (prometheus-community#171) Bumps [k8s.io/client-go](https://github.com/kubernetes/client-go) from 0.24.2 to 0.24.3. - [Release notes](https://github.com/kubernetes/client-go/releases) - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](kubernetes/client-go@v0.24.2...v0.24.3) --- updated-dependencies: - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Signed-off-by: ngrebels <[email protected]> Signed-off-by: Yao Hong Kok <[email protected]> * Bump github.com/prometheus/common from 0.35.0 to 0.37.0 (prometheus-community#170) Bumps [github.com/prometheus/common](https://github.com/prometheus/common) from 0.35.0 to 0.37.0. - [Release notes](https://github.com/prometheus/common/releases) - [Commits](prometheus/common@v0.35.0...v0.37.0) --- updated-dependencies: - dependency-name: github.com/prometheus/common dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Signed-off-by: ngrebels <[email protected]> Signed-off-by: Yao Hong Kok <[email protected]> * Added a value converter for dynamic values and associated tests Signed-off-by: ngrebels <[email protected]> Signed-off-by: Yao Hong Kok <[email protected]> * Refactored into functions and created a type Signed-off-by: ngrebels <[email protected]> Signed-off-by: Yao Hong Kok <[email protected]> * value converter: added example Signed-off-by: ngrebels <[email protected]> Signed-off-by: Yao Hong Kok <[email protected]> * Remove underscore from variable name Signed-off-by: Yao Hong Kok <[email protected]> * Fix formatting error from merging Signed-off-by: Yao Hong Kok <[email protected]> Signed-off-by: ngrebels <[email protected]> Signed-off-by: Yao Hong Kok <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Yao Hong Kok <[email protected]>
1 parent cb16d24 commit cedb5a5

File tree

8 files changed

+232
-0
lines changed

8 files changed

+232
-0
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,18 @@ modules:
6363
active: 1 # static value
6464
count: '{.count}' # dynamic value
6565
boolean: '{.some_boolean}'
66+
67+
- name: example_convert
68+
type: object
69+
path: '{.values[0,1]}'
70+
labels:
71+
state: '{.state}'
72+
values:
73+
state: '{.state}'
74+
valueconverter:
75+
'{.state}': #convert value 'state' into a number
76+
active: 1
77+
inactive: 2
6678

6779
headers:
6880
X-Dummy: my-test-header
@@ -88,6 +100,8 @@ $ ./json_exporter --config.file examples/config.yml &
88100
Validate it's running correctly
89101
```
90102
$ curl "http://localhost:7979/probe?module=default&target=http://localhost:8000/examples/data.json" | grep ^example
103+
example_convert_state{state="ACTIVE"} 1
104+
example_convert_state{state="INACTIVE"} 2
91105
example_global_value{environment="beta",location="planet-mars"} 1234
92106
example_value_active{environment="beta",id="id-A"} 1
93107
example_value_active{environment="beta",id="id-C"} 1

config/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@ type Metric struct {
3030
EpochTimestamp string
3131
Help string
3232
Values map[string]string
33+
ValueConverter ValueConverterType
3334
}
3435

36+
type ValueConverterType map[string]map[string]string
37+
3538
type ScrapeType string
3639

3740
const (

examples/config.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@ modules:
2525
active: 1 # static value
2626
count: '{.count}' # dynamic value
2727
boolean: '{.some_boolean}'
28+
29+
- name: example_convert
30+
type: object
31+
path: '{.values[0,1]}'
32+
labels:
33+
state: '{.state}'
34+
values:
35+
state: '{.state}'
36+
valueconverter:
37+
'{.state}': #convert value 'state' in JSON into a number
38+
active: 1
39+
inactive: 2
40+
2841
headers:
2942
X-Dummy: my-test-header
3043

exporter/collector.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package exporter
1616
import (
1717
"bytes"
1818
"encoding/json"
19+
"strings"
1920
"time"
2021

2122
"github.com/go-kit/log"
@@ -38,6 +39,7 @@ type JSONMetric struct {
3839
ValueJSONPath string
3940
LabelsJSONPaths []string
4041
ValueType prometheus.ValueType
42+
ValueConverter config.ValueConverterType
4143
EpochTimestampJSONPath string
4244
}
4345

@@ -86,11 +88,14 @@ func (mc JSONMetricCollector) Collect(ch chan<- prometheus.Metric) {
8688
continue
8789
}
8890
value, err := extractValue(mc.Logger, jdata, m.ValueJSONPath, false)
91+
8992
if err != nil {
9093
level.Error(mc.Logger).Log("msg", "Failed to extract value for metric", "path", m.ValueJSONPath, "err", err, "metric", m.Desc)
9194
continue
9295
}
9396

97+
value = convertValueIfNeeded(m, value)
98+
9499
if floatValue, err := SanitizeValue(value); err == nil {
95100
metric := prometheus.MustNewConstMetric(
96101
m.Desc,
@@ -161,6 +166,19 @@ func extractLabels(logger log.Logger, data []byte, paths []string) []string {
161166
return labels
162167
}
163168

169+
// Returns the conversion of the dynamic value- if it exists in the ValueConverter configuration
170+
func convertValueIfNeeded(m JSONMetric, value string) string {
171+
if m.ValueConverter != nil {
172+
if valueMappings, hasPathKey := m.ValueConverter[m.ValueJSONPath]; hasPathKey {
173+
value = strings.ToLower(value)
174+
175+
if _, hasValueKey := valueMappings[value]; hasValueKey {
176+
value = valueMappings[value]
177+
}
178+
}
179+
}
180+
return value
181+
}
164182
func timestampMetric(logger log.Logger, m JSONMetric, data []byte, pm prometheus.Metric) prometheus.Metric {
165183
if m.EpochTimestampJSONPath == "" {
166184
return pm

exporter/util.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ func CreateMetricsList(c config.Module) ([]JSONMetric, error) {
118118
variableLabels = append(variableLabels, k)
119119
variableLabelsValues = append(variableLabelsValues, v)
120120
}
121+
122+
var valueConverters config.ValueConverterType = initializeValueConverter(metric)
123+
121124
jsonMetric := JSONMetric{
122125
Type: config.ObjectScrape,
123126
Desc: prometheus.NewDesc(
@@ -130,6 +133,7 @@ func CreateMetricsList(c config.Module) ([]JSONMetric, error) {
130133
ValueJSONPath: valuePath,
131134
LabelsJSONPaths: variableLabelsValues,
132135
ValueType: valueType,
136+
ValueConverter: valueConverters,
133137
EpochTimestampJSONPath: metric.EpochTimestamp,
134138
}
135139
metrics = append(metrics, jsonMetric)
@@ -245,3 +249,22 @@ func renderBody(logger log.Logger, body config.Body, tplValues url.Values) (meth
245249
}
246250
return
247251
}
252+
253+
// Initializes and returns a ValueConverter object. nil if there aren't any conversions
254+
func initializeValueConverter(metric config.Metric) config.ValueConverterType {
255+
var valueConverters config.ValueConverterType
256+
257+
//convert all keys to lowercase
258+
if metric.ValueConverter != nil {
259+
valueConverters = make(config.ValueConverterType)
260+
for valuesKey, innerMap := range metric.ValueConverter {
261+
//make the mappings for each value key lowercase
262+
valueConverters[valuesKey] = make(map[string]string)
263+
for conversionFrom, conversionTo := range innerMap {
264+
valueConverters[valuesKey][strings.ToLower(conversionFrom)] = conversionTo
265+
}
266+
}
267+
}
268+
269+
return valueConverters
270+
}

test/config/test-converter.yml

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
---
2+
#the following tests use ./valueconverter.json
3+
4+
modules:
5+
default:
6+
metrics:
7+
#State should be converted to 1 for the metric value
8+
#Test uppercase key
9+
- name: test1
10+
path: "{$}"
11+
help: Testing Single Value Converter on Type Object
12+
type: object
13+
labels:
14+
name: '{.name}'
15+
values:
16+
state: '{.state}'
17+
valueconverter:
18+
'{.state}':
19+
ACTIVE: 1
20+
21+
#State should be converted to 1 for the metric value
22+
#Test lowercase key
23+
- name: test2
24+
path: "{$}"
25+
help: Testing Single Value Converter on Type Object
26+
type: object
27+
labels:
28+
name: '{.name}'
29+
values:
30+
state: '{.state}'
31+
valueconverter:
32+
'{.state}':
33+
active: 1
34+
35+
#There should be two JSONs returned: a metric for 'state' with value 1, and a metric with value 12
36+
- name: test3
37+
path: "{$}"
38+
help: Testing Multi Diff Value Converter on Type Object
39+
type: object
40+
labels:
41+
name: '{.name}'
42+
values:
43+
state: '{.state}'
44+
active: 12
45+
valueconverter:
46+
'{.state}':
47+
ACTIVE: 1
48+
49+
#Nothing should be returned. This should be an error since 'state' can't be converted to an int
50+
- name: test4
51+
path: "{$}"
52+
help: Testing Value with missing conversion
53+
type: object
54+
labels:
55+
name: '{.name}'
56+
values:
57+
state: '{.state}'
58+
59+
#Test nested JSON. It should return with both values but with 12 as the metric value
60+
- name: test5
61+
path: '{.values[*]}'
62+
help: Testing Conversion with Missing value
63+
type: object
64+
labels:
65+
name: '{.name}'
66+
values:
67+
active: 12
68+
valueconverter:
69+
'{.state}':
70+
ACTIVE: 1
71+
72+
#Test nested JSON.
73+
#It should return with both values but 'state' should be converted
74+
- name: test6
75+
path: '{.values[*]}'
76+
help: Testing Value with Multiple Conversions
77+
type: object
78+
labels:
79+
name: '{.name}'
80+
values:
81+
state: '{.state}'
82+
valueconverter:
83+
'{.state}':
84+
ACTIVE: 1
85+
inactive: 2
86+
87+
#Test nested JSON.
88+
#However, it should only return with state value
89+
#There is a missing key: 'down' in valueconverter
90+
- name: test7
91+
path: '{.values[*]}'
92+
help: Testing Value with Multiple Conversions and Missing Key, Value
93+
type: object
94+
labels:
95+
name: '{.name}'
96+
values:
97+
state: '{.state}'
98+
valueconverter:
99+
'{.state}':
100+
ACTIVE: 1
101+
102+
#Two metrics should be returned
103+
#State should be mapped to 1.
104+
#ResponseCode should be 2 because it already has a value in the JSON.
105+
- name: test8
106+
path: "{$}"
107+
help: Testing Multi Diff Value Converter on Type Object
108+
type: object
109+
labels:
110+
name: '{.name}'
111+
values:
112+
stat: '{.state}'
113+
active: '{.responseCode}'
114+
valueconverter:
115+
'{.state}':
116+
ACTIVE: 1

test/response/test-converter.txt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# HELP test1_state Testing Single Value Converter on Type Object
2+
# TYPE test1_state untyped
3+
test1_state{name="Test Converter"} 1
4+
# HELP test2_state Testing Single Value Converter on Type Object
5+
# TYPE test2_state untyped
6+
test2_state{name="Test Converter"} 1
7+
# HELP test3_active Testing Multi Diff Value Converter on Type Object
8+
# TYPE test3_active untyped
9+
test3_active{name="Test Converter"} 12
10+
# HELP test3_state Testing Multi Diff Value Converter on Type Object
11+
# TYPE test3_state untyped
12+
test3_state{name="Test Converter"} 1
13+
# HELP test5_active Testing Conversion with Missing value
14+
# TYPE test5_active untyped
15+
test5_active{name="id-A"} 12
16+
test5_active{name="id-B"} 12
17+
# HELP test6_state Testing Value with Multiple Conversions
18+
# TYPE test6_state untyped
19+
test6_state{name="id-A"} 1
20+
test6_state{name="id-B"} 2
21+
# HELP test7_state Testing Value with Multiple Conversions and Missing Key, Value
22+
# TYPE test7_state untyped
23+
test7_state{name="id-A"} 1
24+
# HELP test8_active Testing Multi Diff Value Converter on Type Object
25+
# TYPE test8_active untyped
26+
test8_active{name="Test Converter"} 2
27+
# HELP test8_stat Testing Multi Diff Value Converter on Type Object
28+
# TYPE test8_stat untyped
29+
test8_stat{name="Test Converter"} 1

test/serve/test-converter.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "Test Converter",
3+
"state": "ACTIVE",
4+
"responseCode": 2,
5+
"values": [
6+
{
7+
"name": "id-A",
8+
"state": "ACTIVE"
9+
},
10+
{
11+
"name": "id-B",
12+
"state": "INACTIVE"
13+
}
14+
]
15+
}
16+

0 commit comments

Comments
 (0)