-
Notifications
You must be signed in to change notification settings - Fork 5k
Report kibana_settings to X-Pack Monitoring #7664
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
Changes from 3 commits
86cfe01
a59d59d
828ba1d
9101c9d
011b7cf
3afa1a2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| { | ||
|
||
| "cluster_uuid":"u5ii0pnQRka_P0gimfmthg", | ||
| "settings":{ | ||
| "xpack":{ | ||
| "default_admin_email":"[email protected]" | ||
| }, | ||
| "kibana":{ | ||
| "uuid":"5b2de169-2785-441b-ae8c-186a1936b17d", | ||
| "name":"Janes-MBP-2", | ||
| "index":".kibana", | ||
| "host":"localhost", | ||
| "transport_address":"localhost:5601", | ||
| "version":"7.0.0-alpha1", | ||
| "snapshot":false, | ||
| "status":"green" | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,7 +29,7 @@ import ( | |
| ) | ||
|
|
||
| var ( | ||
| schemaXPackMonitoring = s.Schema{ | ||
| schemaXPackMonitoringStats = s.Schema{ | ||
| "concurrent_connections": c.Int("concurrent_connections"), | ||
| "os": c.Dict("os", s.Schema{ | ||
| "load": c.Dict("load", s.Schema{ | ||
|
|
@@ -105,61 +105,91 @@ var ( | |
| } | ||
| ) | ||
|
|
||
| func eventMappingXPack(r mb.ReporterV2, intervalMs int64, content []byte) error { | ||
| var data map[string]interface{} | ||
| err := json.Unmarshal(content, &data) | ||
| if err != nil { | ||
| r.Error(err) | ||
| return err | ||
| type dataParser func(mb.ReporterV2, common.MapStr, time.Time) (string, string, common.MapStr, error) | ||
|
|
||
| func statsDataParser(r mb.ReporterV2, data common.MapStr, now time.Time) (string, string, common.MapStr, error) { | ||
| clusterUUID, ok := data["clusterUuid"].(string) | ||
| if !ok { | ||
| return "", "", nil, elastic.ReportErrorForMissingField("clusterUuid", elastic.Kibana, r) | ||
| } | ||
|
|
||
| kibanaStatsFields, err := schemaXPackMonitoring.Apply(data) | ||
| kibanaStatsFields, err := schemaXPackMonitoringStats.Apply(data) | ||
| if err != nil { | ||
| r.Error(err) | ||
| return err | ||
| return "", "", nil, err | ||
| } | ||
|
|
||
| process, ok := data["process"].(map[string]interface{}) | ||
| if !ok { | ||
| return elastic.ReportErrorForMissingField("process", elastic.Kibana, r) | ||
| return "", "", nil, elastic.ReportErrorForMissingField("process", elastic.Kibana, r) | ||
| } | ||
| memory, ok := process["memory"].(map[string]interface{}) | ||
| if !ok { | ||
| return elastic.ReportErrorForMissingField("process.memory", elastic.Kibana, r) | ||
| return "", "", nil, elastic.ReportErrorForMissingField("process.memory", elastic.Kibana, r) | ||
| } | ||
|
|
||
| rss, ok := memory["resident_set_size_bytes"].(float64) | ||
| if !ok { | ||
| return elastic.ReportErrorForMissingField("process.memory.resident_set_size_bytes", elastic.Kibana, r) | ||
| return "", "", nil, elastic.ReportErrorForMissingField("process.memory.resident_set_size_bytes", elastic.Kibana, r) | ||
| } | ||
| kibanaStatsFields.Put("process.memory.resident_set_size_in_bytes", int64(rss)) | ||
|
|
||
| timestamp := time.Now() | ||
| kibanaStatsFields.Put("timestamp", timestamp) | ||
| kibanaStatsFields.Put("timestamp", now) | ||
|
|
||
| // Make usage field passthrough as-is | ||
| usage, ok := data["usage"].(map[string]interface{}) | ||
| if !ok { | ||
| return elastic.ReportErrorForMissingField("usage", elastic.Kibana, r) | ||
| return "", "", nil, elastic.ReportErrorForMissingField("usage", elastic.Kibana, r) | ||
| } | ||
| kibanaStatsFields.Put("usage", usage) | ||
|
|
||
| clusterUUID, ok := data["clusterUuid"].(string) | ||
| return "kibana_stats", clusterUUID, kibanaStatsFields, nil | ||
| } | ||
|
|
||
| func settingsDataParser(r mb.ReporterV2, data common.MapStr, now time.Time) (string, string, common.MapStr, error) { | ||
| clusterUUID, ok := data["cluster_uuid"].(string) | ||
| if !ok { | ||
| return elastic.ReportErrorForMissingField("clusterUuid", elastic.Kibana, r) | ||
| return "", "", nil, elastic.ReportErrorForMissingField("cluster_uuid", elastic.Kibana, r) | ||
| } | ||
|
|
||
| kibanaSettingsFields, ok := data["settings"] | ||
| if !ok { | ||
| return "", "", nil, elastic.ReportErrorForMissingField("settings", elastic.Kibana, r) | ||
| } | ||
|
|
||
| return "kibana_settings", clusterUUID, kibanaSettingsFields.(map[string]interface{}), nil | ||
| } | ||
|
|
||
| func eventMappingXPack(r mb.ReporterV2, intervalMs int64, now time.Time, content []byte, dataParserFunc dataParser) error { | ||
| var data map[string]interface{} | ||
| err := json.Unmarshal(content, &data) | ||
| if err != nil { | ||
| r.Error(err) | ||
|
||
| return err | ||
| } | ||
|
|
||
| t, clusterUUID, fields, err := dataParserFunc(r, data, now) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| var event mb.Event | ||
| event.RootFields = common.MapStr{ | ||
| "cluster_uuid": clusterUUID, | ||
| "timestamp": timestamp, | ||
| "timestamp": now, | ||
| "interval_ms": intervalMs, | ||
| "type": "kibana_stats", | ||
| "kibana_stats": kibanaStatsFields, | ||
| "type": t, | ||
| t: fields, | ||
| } | ||
|
|
||
| event.Index = elastic.MakeXPackMonitoringIndexName(elastic.Kibana) | ||
| r.Event(event) | ||
|
|
||
| r.Event(event) | ||
| return nil | ||
| } | ||
|
|
||
| func eventMappingStatsXPack(r mb.ReporterV2, intervalMs int64, now time.Time, content []byte) error { | ||
| return eventMappingXPack(r, intervalMs, now, content, statsDataParser) | ||
| } | ||
|
|
||
| func eventMappingSettingsXPack(r mb.ReporterV2, intervalMs int64, now time.Time, content []byte) error { | ||
| return eventMappingXPack(r, intervalMs, now, content, settingsDataParser) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,8 @@ package stats | |
|
|
||
| import ( | ||
| "fmt" | ||
| "strings" | ||
| "time" | ||
|
|
||
| "github.com/elastic/beats/libbeat/common/cfgwarn" | ||
| "github.com/elastic/beats/metricbeat/helper" | ||
|
|
@@ -36,7 +38,8 @@ func init() { | |
| } | ||
|
|
||
| const ( | ||
| statsPath = "api/stats" | ||
| statsPath = "api/stats" | ||
|
||
| settingsPath = "api/settings" | ||
| ) | ||
|
|
||
| var ( | ||
|
|
@@ -50,7 +53,8 @@ var ( | |
| // MetricSet type defines all fields of the MetricSet | ||
| type MetricSet struct { | ||
| mb.BaseMetricSet | ||
| http *helper.HTTP | ||
| statsHTTP *helper.HTTP | ||
| settingsHTTP *helper.HTTP | ||
| xPackEnabled bool | ||
| } | ||
|
|
||
|
|
@@ -63,22 +67,22 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { | |
| return nil, err | ||
| } | ||
|
|
||
| http, err := helper.NewHTTP(base) | ||
| statsHTTP, err := helper.NewHTTP(base) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| kibanaVersion, err := kibana.GetVersion(http, statsPath) | ||
| kibanaVersion, err := kibana.GetVersion(statsHTTP, statsPath) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One thing I just realised that bugs me a bit with this implementation is that it requires Kibana to be up and running normally when Metricbeat is started. Assuming Kibana is flaky during the moment the check happens, the metricset will not be running but error out even though Kibana could be totally fine again 1 sec later. Not for this PR but thought worth mentioning.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. I suppose we could move this check (and the code dependent on it) into
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we need another metricset lifecycle method in addition to By default this In the case of this metricset, we would move the Kibana version checking (and dependent) code into Thoughts? I agree that this would be beyond the scope of this PR but, if you like it, I can make a separate issue/followup PR for it.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about using fetch but do it only once? I remember we do something like that in other cases.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Merged as it's something we should discuss as a follow up. |
||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| isAPIAvailable, err := kibana.IsStatsAPIAvailable(kibanaVersion) | ||
| isStatsAPIAvailable, err := kibana.IsStatsAPIAvailable(kibanaVersion) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| if !isAPIAvailable { | ||
| if !isStatsAPIAvailable { | ||
| const errorMsg = "The kibana stats metricset is only supported with Kibana >= %v. You are currently running Kibana %v" | ||
| return nil, fmt.Errorf(errorMsg, kibana.StatsAPIAvailableVersion, kibanaVersion) | ||
| } | ||
|
|
@@ -87,12 +91,38 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { | |
| cfgwarn.Experimental("The experimental xpack.enabled flag in kibana/stats metricset is enabled.") | ||
|
|
||
| // Use legacy API response so we can passthru usage as-is | ||
| http.SetURI(http.GetURI() + "&legacy=true") | ||
| statsHTTP.SetURI(statsHTTP.GetURI() + "&legacy=true") | ||
| } | ||
|
|
||
| var settingsHTTP *helper.HTTP | ||
| if config.XPackEnabled { | ||
| cfgwarn.Experimental("The experimental xpack.enabled flag in kibana/stats metricset is enabled.") | ||
|
|
||
| isSettingsAPIAvailable, err := kibana.IsSettingsAPIAvailable(kibanaVersion) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| if !isSettingsAPIAvailable { | ||
| const errorMsg = "The kibana stats metricset with X-Pack enabled is only supported with Kibana >= %v. You are currently running Kibana %v" | ||
| return nil, fmt.Errorf(errorMsg, kibana.SettingsAPIAvailableVersion, kibanaVersion) | ||
| } | ||
|
|
||
| settingsHTTP, err = helper.NewHTTP(base) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| // HACK! We need to do this because there might be a basepath involved, so we | ||
| // only search/replace the actual API paths | ||
| settingsURI := strings.Replace(statsHTTP.GetURI(), statsPath, settingsPath, 1) | ||
| settingsHTTP.SetURI(settingsURI) | ||
| } | ||
|
|
||
| return &MetricSet{ | ||
| base, | ||
| http, | ||
| statsHTTP, | ||
| settingsHTTP, | ||
| config.XPackEnabled, | ||
| }, nil | ||
| } | ||
|
|
@@ -101,17 +131,35 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { | |
| // It returns the event which is then forward to the output. In case of an error, a | ||
| // descriptive error must be returned. | ||
| func (m *MetricSet) Fetch(r mb.ReporterV2) { | ||
| content, err := m.http.FetchContent() | ||
| intervalMs := m.Module().Config().Period.Nanoseconds() / 1000 / 1000 | ||
|
||
| now := time.Now() | ||
|
|
||
| m.fetchStats(r, intervalMs, now) | ||
| if m.xPackEnabled { | ||
| m.fetchSettings(r, intervalMs, now) | ||
| } | ||
| } | ||
|
|
||
| func (m *MetricSet) fetchStats(r mb.ReporterV2, intervalMs int64, now time.Time) { | ||
| content, err := m.statsHTTP.FetchContent() | ||
| if err != nil { | ||
| r.Error(err) | ||
| return | ||
| } | ||
|
|
||
| if m.xPackEnabled { | ||
| intervalMs := m.Module().Config().Period.Nanoseconds() / 1000 / 1000 | ||
| eventMappingXPack(r, intervalMs, content) | ||
| eventMappingStatsXPack(r, intervalMs, now, content) | ||
| } else { | ||
| eventMapping(r, content) | ||
| } | ||
| } | ||
|
|
||
| func (m *MetricSet) fetchSettings(r mb.ReporterV2, intervalMs int64, now time.Time) { | ||
| content, err := m.settingsHTTP.FetchContent() | ||
| if err != nil { | ||
| r.Error(err) | ||
|
||
| return | ||
| } | ||
|
|
||
| eventMappingSettingsXPack(r, intervalMs, now, content) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this package still somewhere used?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It shouldn't be any more. This section probably got re-introduced during a rebase or something. I will remove it. Thanks for catching!