Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: count json objects with same value #149

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ Serving HTTP on :: port 8000 (http://[::]:8000/) ...
## TEST with 'default' module

$ curl "http://localhost:7979/probe?module=default&target=http://localhost:8000/examples/data.json"
# 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 untyped
example_global_value{environment="beta",location="planet-mars"} 1234
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
MinimumCount 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].MinimumCount > 0) {
module.Metrics[i].MinimumCount = 1
}
}
}

return config, nil
}
10 changes: 9 additions & 1 deletion examples/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ 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
# The minimum count of labels required to expose the metric for each label. Defaults to 1.
minimumCount: 1

animals:
metrics:
Expand Down Expand Up @@ -66,4 +75,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
MinimumCount 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.MinimumCount {
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,
MinimumCount: metric.MinimumCount,
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