Skip to content

Commit

Permalink
feature: count json objects with same value
Browse files Browse the repository at this point in the history
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]>
Signed-off-by: Parsa <[email protected]>
  • Loading branch information
Parsa authored and parsa97 committed Jun 6, 2023
1 parent 7ab7efc commit 5c2d471
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 2 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ Serving HTTP on :: port 8000 (http://[::]:8000/) ...
$ curl "http://localhost:7979/probe?module=default&target=http://localhost:8000/examples/data.json"
# HELP example_global_value Example of a top-level global value scrape in the json
# TYPE example_global_value untyped
$ curl "http://localhost:7979/probe?target=http://localhost:8000/examples/data.json" | grep ^example
example_count{environment="beta",state="ACTIVE"} 2
example_count{environment="beta",state="INACTIVE"} 1
example_global_value{environment="beta",location="planet-mars"} 1234
# HELP example_timestamped_value_count Example of a timestamped value scrape in the json
# TYPE example_timestamped_value_count untyped
Expand Down
6 changes: 5 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ type Metric struct {
EpochTimestamp string
Help string
Values map[string]string
Mincount int
}

type ScrapeType string

const (
ValueScrape ScrapeType = "value" // default
ObjectScrape ScrapeType = "object"
CountScrape ScrapeType = "countbylabel"
)

type ValueType string
Expand Down Expand Up @@ -89,8 +91,10 @@ func LoadConfig(configPath string) (Config, error) {
if module.Metrics[i].ValueType == "" {
module.Metrics[i].ValueType = ValueTypeUntyped
}
if !(module.Metrics[i].Mincount > 0) {
module.Metrics[i].Mincount = 1
}
}
}

return config, nil
}
9 changes: 8 additions & 1 deletion examples/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ modules:
active: 1 # static value
count: '{.count}' # dynamic value
boolean: '{.some_boolean}'
- name: example_count
type: countbylabel
help: Example of count json labels
path: '{.values[*].state}'
labels:
environment: beta # static label
state: '{}' # dynamic label
mincount: 1

animals:
metrics:
Expand Down Expand Up @@ -66,4 +74,3 @@ modules:
# content: |
# {"time_diff": "{{ duration `95` }}","anotherVar": "{{ .myVal | first }}"}
# templatize: true

37 changes: 37 additions & 0 deletions exporter/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type JSONMetric struct {
LabelsJSONPaths []string
ValueType prometheus.ValueType
EpochTimestampJSONPath string
Mincount int
}

func (mc JSONMetricCollector) Describe(ch chan<- *prometheus.Desc) {
Expand Down Expand Up @@ -70,6 +71,42 @@ func (mc JSONMetricCollector) Collect(ch chan<- prometheus.Metric) {
continue
}

case config.CountScrape:
values, err := extractValue(mc.Logger, mc.Data, m.KeyJSONPath, true)
if err != nil {
level.Error(mc.Logger).Log("msg", "Failed to extract json objects for metric", "err", err, "metric", m.Desc)
continue
}

var jsonData []interface{}
counts := make(map[interface{}]int)

if err := json.Unmarshal([]byte(values), &jsonData); err == nil {
for _, data := range jsonData {
counts[data]++
}
for data, count := range counts {
if count >= m.Mincount {
jdata, err := json.Marshal(data)
if err != nil {
level.Error(mc.Logger).Log("msg", "Failed to marshal data to json", "path", m.ValueJSONPath, "err", err, "metric", m.Desc, "data", data)
continue
}
if err != nil {
level.Error(mc.Logger).Log("msg", "Failed to extract value for metric", "path", m.ValueJSONPath, "err", err, "metric", m.Desc)
continue
}

ch <- prometheus.MustNewConstMetric(
m.Desc,
prometheus.UntypedValue,
float64(count),
extractLabels(mc.Logger, jdata, m.LabelsJSONPaths)...,
)
}
}
}

case config.ObjectScrape:
values, err := extractValue(mc.Logger, mc.Data, m.KeyJSONPath, true)
if err != nil {
Expand Down
22 changes: 22 additions & 0 deletions exporter/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,28 @@ func CreateMetricsList(c config.Module) ([]JSONMetric, error) {
}
metrics = append(metrics, jsonMetric)
}
case config.CountScrape:
var variableLabels, variableLabelsValues []string
for k, v := range metric.Labels {
variableLabels = append(variableLabels, k)
variableLabelsValues = append(variableLabelsValues, v)
}
jsonMetric := JSONMetric{
Type: config.CountScrape,
Desc: prometheus.NewDesc(
metric.Name,
metric.Help,
variableLabels,
nil,
),
KeyJSONPath: metric.Path,
Mincount: metric.Mincount,
LabelsJSONPaths: variableLabelsValues,
ValueType: valueType,
EpochTimestampJSONPath: metric.EpochTimestamp,
}
fmt.Println(jsonMetric)
metrics = append(metrics, jsonMetric)
default:
return nil, fmt.Errorf("Unknown metric type: '%s', for metric: '%s'", metric.Type, metric.Name)
}
Expand Down
8 changes: 8 additions & 0 deletions test/config/good.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,11 @@ modules:
active: 1 # static value
count: '{.count}' # dynamic value
boolean: '{.some_boolean}'

- name: example_count
type: countbylabel
help: Example of count object from a json
path: '{.values[*].state}'
labels:
environment: beta # static label
state: '{}' # dynamic label
4 changes: 4 additions & 0 deletions test/response/good.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# HELP example_count Example of count object from a json
# TYPE example_count untyped
example_count{environment="beta",state="ACTIVE"} 2
example_count{environment="beta",state="INACTIVE"} 1
# HELP example_global_value Example of a top-level global value scrape in the json
# TYPE example_global_value gauge
example_global_value{environment="beta",location="planet-mars"} 1234
Expand Down

0 comments on commit 5c2d471

Please sign in to comment.