Skip to content

Commit 142b859

Browse files
Carlos Pérez-Aradros HerceChrsMarkjsoriano
authored
Use Elasticsearch histogram type to store Prometheus histograms (#17061)
* Allow to override already defined metricsets This is useful when we want to have different behaviors between flavours * Add new Prometheus skeleton for basic types usage * Refactor Prometheus module to accommodate newer uses * Add typed schema to Prometheus module This should make room for using different Elasticsearch types depending on the Prometheus metric * Convert Prometheus histograms to ES histograms Co-authored-by: Chris Mark <chrismarkou92@gmail.com> Co-authored-by: Jaime Soriano Pastor <jaime.soriano@elastic.co>
1 parent 0e86555 commit 142b859

35 files changed

Lines changed: 2102 additions & 108 deletions

CHANGELOG.next.asciidoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,8 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d
262262
- Add support for CouchDB v2 {issue}16352[16352] {pull}16455[16455]
263263
- Add dashboards for the azure container metricsets. {pull}17194[17194]
264264
- Replace vpc metricset into vpn, transitgateway and natgateway metricsets. {pull}16892[16892]
265+
- Use Elasticsearch histogram type to store Prometheus histograms {pull}17061[17061]
266+
- Allow to rate Prometheus counters when scraping them {pull}17061[17061]
265267
- Release Oracle module as GA. {issue}14279[14279] {pull}16833[16833]
266268
- Release vsphere module as GA. {issue}15798[15798] {pull}17119[17119]
267269
- Add Storage metricsets to GCP module {pull}15598[15598]

metricbeat/docs/fields.asciidoc

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ grouped in the following categories:
6666
* <<exported-fields-postgresql>>
6767
* <<exported-fields-process>>
6868
* <<exported-fields-prometheus>>
69+
* <<exported-fields-prometheus-xpack>>
6970
* <<exported-fields-rabbitmq>>
7071
* <<exported-fields-redis>>
7172
* <<exported-fields-redisenterprise>>
@@ -31639,6 +31640,53 @@ query metricset
3163931640
remote write metrics from Prometheus server
3164031641
3164131642
31643+
[[exported-fields-prometheus-xpack]]
31644+
== Prometheus typed metrics fields
31645+
31646+
Stats scraped from a Prometheus endpoint.
31647+
31648+
31649+
31650+
*`prometheus.*.value`*::
31651+
+
31652+
--
31653+
Prometheus gauge metric
31654+
31655+
31656+
type: object
31657+
31658+
--
31659+
31660+
*`prometheus.*.counter`*::
31661+
+
31662+
--
31663+
Prometheus counter metric
31664+
31665+
31666+
type: object
31667+
31668+
--
31669+
31670+
*`prometheus.*.rate`*::
31671+
+
31672+
--
31673+
Prometheus rated counter metric
31674+
31675+
31676+
type: object
31677+
31678+
--
31679+
31680+
*`prometheus.*.histogram`*::
31681+
+
31682+
--
31683+
Prometheus histogram metric - release: ga
31684+
31685+
31686+
type: object
31687+
31688+
--
31689+
3164231690
[[exported-fields-rabbitmq]]
3164331691
== RabbitMQ fields
3164431692

metricbeat/mb/registry.go

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ type MetricSetRegistration struct {
6868
IsDefault bool
6969
HostParser HostParser
7070
Namespace string
71+
Replace bool
7172
}
7273

7374
// MetricSetOption sets an option for a MetricSetFactory that is being
@@ -99,6 +100,15 @@ func WithNamespace(namespace string) MetricSetOption {
99100
}
100101
}
101102

103+
// MustReplace specifies that the MetricSetFactory must be replacing an existing
104+
// metricset with the same name. An error will happen if there is no metricset
105+
// defined with the same params.
106+
func MustReplace() MetricSetOption {
107+
return func(r *MetricSetRegistration) {
108+
r.Replace = true
109+
}
110+
}
111+
102112
// Register contains the factory functions for creating new Modules and new
103113
// MetricSets. Registers are thread safe for concurrent usage.
104114
type Register struct {
@@ -201,22 +211,28 @@ func (r *Register) addMetricSet(module, name string, factory MetricSetFactory, o
201211
module = strings.ToLower(module)
202212
name = strings.ToLower(name)
203213

214+
// Set the options.
215+
msInfo := MetricSetRegistration{Name: name, Factory: factory}
216+
for _, opt := range options {
217+
opt(&msInfo)
218+
}
219+
204220
if metricsets, ok := r.metricSets[module]; !ok {
221+
if msInfo.Replace {
222+
return fmt.Errorf("metricset '%s/%s' should be replacing an existing metricset, none found", module, name)
223+
}
224+
205225
r.metricSets[module] = map[string]MetricSetRegistration{}
206226
} else if _, exists := metricsets[name]; exists {
207-
return fmt.Errorf("metricset '%s/%s' is already registered", module, name)
227+
if !msInfo.Replace {
228+
return fmt.Errorf("metricset '%s/%s' is already registered", module, name)
229+
}
208230
}
209231

210232
if factory == nil {
211233
return fmt.Errorf("metricset '%s/%s' cannot be registered with a nil factory", module, name)
212234
}
213235

214-
// Set the options.
215-
msInfo := MetricSetRegistration{Name: name, Factory: factory}
216-
for _, opt := range options {
217-
opt(&msInfo)
218-
}
219-
220236
r.metricSets[module][name] = msInfo
221237
r.log.Infof("MetricSet registered: %s/%s", module, name)
222238
return nil

metricbeat/mb/registry_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,14 @@ func TestDefaultMetricSet(t *testing.T) {
192192
assert.Contains(t, names, metricSetName)
193193
}
194194

195+
func TestMustReplaceMetricSet(t *testing.T) {
196+
registry := NewRegister()
197+
err := registry.addMetricSet(moduleName, metricSetName, fakeMetricSetFactory, MustReplace())
198+
if assert.Error(t, err) {
199+
assert.Equal(t, "metricset 'mymodule/mymetricset' should be replacing an existing metricset, none found", err.Error())
200+
}
201+
}
202+
195203
func TestMetricSetQuery(t *testing.T) {
196204
registry := NewRegister()
197205
err := registry.AddMetricSet(moduleName, metricSetName, fakeMetricSetFactory)

metricbeat/mb/testing/testdata.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,9 +336,22 @@ func documentedFieldCheck(foundKeys common.MapStr, knownKeys map[string]interfac
336336
return nil
337337
}
338338
}
339-
// If a field is defined as object it can also be defined as `status_codes.*`
340-
// So this checks if such a key with the * exists by removing the last part.
339+
// If a field is defined as object it can also have a * somewhere
340+
// So this checks if such a key with the * exists by testing with it
341341
splits := strings.Split(foundKey, ".")
342+
found := false
343+
for pos := 1; pos < len(splits)-1; pos++ {
344+
key := strings.Join(splits[0:pos], ".") + ".*." + strings.Join(splits[pos+1:len(splits)], ".")
345+
if _, ok := knownKeys[key]; ok {
346+
found = true
347+
break
348+
}
349+
}
350+
if found {
351+
continue
352+
}
353+
354+
// last case `status_codes.*`:
342355
prefix := strings.Join(splits[0:len(splits)-1], ".")
343356
if _, ok := knownKeys[prefix+".*"]; ok {
344357
continue

metricbeat/module/prometheus/collector/_meta/docs.asciidoc

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,58 @@ to retrieve the metrics from (`/metrics` by default) can be configured with `met
2424
-------------------------------------------------------------------------------------
2525

2626

27+
[float]
28+
[role="xpack"]
29+
=== Histograms and types
30+
31+
beta[]
32+
33+
[source,yaml]
34+
-------------------------------------------------------------------------------------
35+
metricbeat.modules:
36+
- module: prometheus
37+
period: 10s
38+
hosts: ["localhost:9090"]
39+
use_types: true
40+
rate_counters: false
41+
-------------------------------------------------------------------------------------
42+
43+
`use_types` paramater (default: false) enables a different layout for metrics storage, leveraging Elasticsearch
44+
types, including https://www.elastic.co/guide/en/elasticsearch/reference/current/histogram.html[histograms].
45+
46+
`rate_counters` paramater (default: false) enables calculating a rate out of Prometheus counters. When enabled, Metricbeat stores
47+
the counter increment since the last collection. This metric should make some aggregations easier and with better
48+
performance. This parameter can only be enabled in combination with `use_types`.
49+
50+
When `use_types` and `rate_counters` are enabled, metrics are stored like this:
51+
52+
[source,json]
53+
----
54+
{
55+
"prometheus": {
56+
"labels": {
57+
"instance": "172.27.0.2:9090",
58+
"job": "prometheus"
59+
},
60+
"prometheus_target_interval_length_seconds_count": {
61+
"counter": 1,
62+
"rate": 0
63+
},
64+
"prometheus_target_interval_length_seconds_sum": {
65+
"counter": 15.000401344,
66+
"rate": 0
67+
}
68+
"prometheus_tsdb_compaction_chunk_range_seconds_bucket": {
69+
"histogram": {
70+
"values": [50, 300, 1000, 4000, 16000],
71+
"counts": [10, 2, 34, 7]
72+
}
73+
}
74+
},
75+
}
76+
----
77+
78+
2779
[float]
2880
=== Scraping all metrics from a Prometheus server
2981

0 commit comments

Comments
 (0)