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

Cherry-pick #17061 to 7.x: Use Elasticsearch histogram type to store Prometheus histograms #17227

Merged
merged 2 commits into from
Mar 25, 2020
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,8 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d
- Release Zookeeper/connection module as GA. {issue}14281[14281] {pull}17043[17043]
- Add dashboards for the azure container metricsets. {pull}17194[17194]
- Replace vpc metricset into vpn, transitgateway and natgateway metricsets. {pull}16892[16892]
- Use Elasticsearch histogram type to store Prometheus histograms {pull}17061[17061]
- Allow to rate Prometheus counters when scraping them {pull}17061[17061]
- Release Oracle module as GA. {issue}14279[14279] {pull}16833[16833]
- Add Storage metricsets to GCP module {pull}15598[15598]
- Release vsphere module as GA. {issue}15798[15798] {pull}17119[17119]
Expand Down
48 changes: 48 additions & 0 deletions metricbeat/docs/fields.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ grouped in the following categories:
* <<exported-fields-postgresql>>
* <<exported-fields-process>>
* <<exported-fields-prometheus>>
* <<exported-fields-prometheus-xpack>>
* <<exported-fields-rabbitmq>>
* <<exported-fields-redis>>
* <<exported-fields-redisenterprise>>
Expand Down Expand Up @@ -31639,6 +31640,53 @@ query metricset
remote write metrics from Prometheus server


[[exported-fields-prometheus-xpack]]
== Prometheus typed metrics fields

Stats scraped from a Prometheus endpoint.



*`prometheus.*.value`*::
+
--
Prometheus gauge metric


type: object

--

*`prometheus.*.counter`*::
+
--
Prometheus counter metric


type: object

--

*`prometheus.*.rate`*::
+
--
Prometheus rated counter metric


type: object

--

*`prometheus.*.histogram`*::
+
--
Prometheus histogram metric - release: ga


type: object

--

[[exported-fields-rabbitmq]]
== RabbitMQ fields

Expand Down
30 changes: 23 additions & 7 deletions metricbeat/mb/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ type MetricSetRegistration struct {
IsDefault bool
HostParser HostParser
Namespace string
Replace bool
}

// MetricSetOption sets an option for a MetricSetFactory that is being
Expand Down Expand Up @@ -99,6 +100,15 @@ func WithNamespace(namespace string) MetricSetOption {
}
}

// MustReplace specifies that the MetricSetFactory must be replacing an existing
// metricset with the same name. An error will happen if there is no metricset
// defined with the same params.
func MustReplace() MetricSetOption {
return func(r *MetricSetRegistration) {
r.Replace = true
}
}

// Register contains the factory functions for creating new Modules and new
// MetricSets. Registers are thread safe for concurrent usage.
type Register struct {
Expand Down Expand Up @@ -201,22 +211,28 @@ func (r *Register) addMetricSet(module, name string, factory MetricSetFactory, o
module = strings.ToLower(module)
name = strings.ToLower(name)

// Set the options.
msInfo := MetricSetRegistration{Name: name, Factory: factory}
for _, opt := range options {
opt(&msInfo)
}

if metricsets, ok := r.metricSets[module]; !ok {
if msInfo.Replace {
return fmt.Errorf("metricset '%s/%s' should be replacing an existing metricset, none found", module, name)
}

r.metricSets[module] = map[string]MetricSetRegistration{}
} else if _, exists := metricsets[name]; exists {
return fmt.Errorf("metricset '%s/%s' is already registered", module, name)
if !msInfo.Replace {
return fmt.Errorf("metricset '%s/%s' is already registered", module, name)
}
}

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

// Set the options.
msInfo := MetricSetRegistration{Name: name, Factory: factory}
for _, opt := range options {
opt(&msInfo)
}

r.metricSets[module][name] = msInfo
r.log.Infof("MetricSet registered: %s/%s", module, name)
return nil
Expand Down
8 changes: 8 additions & 0 deletions metricbeat/mb/registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,14 @@ func TestDefaultMetricSet(t *testing.T) {
assert.Contains(t, names, metricSetName)
}

func TestMustReplaceMetricSet(t *testing.T) {
registry := NewRegister()
err := registry.addMetricSet(moduleName, metricSetName, fakeMetricSetFactory, MustReplace())
if assert.Error(t, err) {
assert.Equal(t, "metricset 'mymodule/mymetricset' should be replacing an existing metricset, none found", err.Error())
}
}

func TestMetricSetQuery(t *testing.T) {
registry := NewRegister()
err := registry.AddMetricSet(moduleName, metricSetName, fakeMetricSetFactory)
Expand Down
17 changes: 15 additions & 2 deletions metricbeat/mb/testing/testdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,9 +336,22 @@ func documentedFieldCheck(foundKeys common.MapStr, knownKeys map[string]interfac
return nil
}
}
// If a field is defined as object it can also be defined as `status_codes.*`
// So this checks if such a key with the * exists by removing the last part.
// If a field is defined as object it can also have a * somewhere
// So this checks if such a key with the * exists by testing with it
splits := strings.Split(foundKey, ".")
found := false
for pos := 1; pos < len(splits)-1; pos++ {
key := strings.Join(splits[0:pos], ".") + ".*." + strings.Join(splits[pos+1:len(splits)], ".")
if _, ok := knownKeys[key]; ok {
found = true
break
}
}
if found {
continue
}

// last case `status_codes.*`:
prefix := strings.Join(splits[0:len(splits)-1], ".")
if _, ok := knownKeys[prefix+".*"]; ok {
continue
Expand Down
52 changes: 52 additions & 0 deletions metricbeat/module/prometheus/collector/_meta/docs.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,58 @@ to retrieve the metrics from (`/metrics` by default) can be configured with `met
-------------------------------------------------------------------------------------


[float]
[role="xpack"]
=== Histograms and types

beta[]

[source,yaml]
-------------------------------------------------------------------------------------
metricbeat.modules:
- module: prometheus
period: 10s
hosts: ["localhost:9090"]
use_types: true
rate_counters: false
-------------------------------------------------------------------------------------

`use_types` paramater (default: false) enables a different layout for metrics storage, leveraging Elasticsearch
types, including https://www.elastic.co/guide/en/elasticsearch/reference/current/histogram.html[histograms].

`rate_counters` paramater (default: false) enables calculating a rate out of Prometheus counters. When enabled, Metricbeat stores
the counter increment since the last collection. This metric should make some aggregations easier and with better
performance. This parameter can only be enabled in combination with `use_types`.

When `use_types` and `rate_counters` are enabled, metrics are stored like this:

[source,json]
----
{
"prometheus": {
"labels": {
"instance": "172.27.0.2:9090",
"job": "prometheus"
},
"prometheus_target_interval_length_seconds_count": {
"counter": 1,
"rate": 0
},
"prometheus_target_interval_length_seconds_sum": {
"counter": 15.000401344,
"rate": 0
}
"prometheus_tsdb_compaction_chunk_range_seconds_bucket": {
"histogram": {
"values": [50, 300, 1000, 4000, 16000],
"counts": [10, 2, 34, 7]
}
}
},
}
----


[float]
=== Scraping all metrics from a Prometheus server

Expand Down
Loading