From 6de73971a0da45e2d418299342d9f7e6a358281b Mon Sep 17 00:00:00 2001 From: Adam Boguszewski <108867528+aboguszewski-sumo@users.noreply.github.com> Date: Thu, 3 Nov 2022 14:42:04 +0100 Subject: [PATCH] [receiver/elasticsearch]: add metrics related to GET operations (#14793) feat: add metrics related to GET operations --- .chloggen/elasticsearch-receiver-get-ops.yaml | 5 + .../elasticsearchreceiver/documentation.md | 3 + .../internal/metadata/generated_metrics.go | 156 ++++++++++++++++++ .../internal/model/nodestats.go | 8 +- receiver/elasticsearchreceiver/metadata.yaml | 24 +++ receiver/elasticsearchreceiver/scraper.go | 6 + .../elasticsearchreceiver/scraper_test.go | 4 + .../testdata/expected_metrics/full.json | 74 +++++++++ 8 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 .chloggen/elasticsearch-receiver-get-ops.yaml diff --git a/.chloggen/elasticsearch-receiver-get-ops.yaml b/.chloggen/elasticsearch-receiver-get-ops.yaml new file mode 100644 index 000000000000..cd18eecd4347 --- /dev/null +++ b/.chloggen/elasticsearch-receiver-get-ops.yaml @@ -0,0 +1,5 @@ +change_type: enhancement +component: elasticsearchreceiver +note: Add metrics related to GET operations +issues: [14635] + diff --git a/receiver/elasticsearchreceiver/documentation.md b/receiver/elasticsearchreceiver/documentation.md index f9e5c2dd25f0..44531f17b6a5 100644 --- a/receiver/elasticsearchreceiver/documentation.md +++ b/receiver/elasticsearchreceiver/documentation.md @@ -53,6 +53,8 @@ These are the metrics available for this scraper. | **elasticsearch.node.ingest.operations.failed** | Total number of failed ingest operations during the lifetime of this node. | {operation} | Sum(Int) | | | **elasticsearch.node.open_files** | The number of open file descriptors held by the node. | {files} | Sum(Int) | | | **elasticsearch.node.operations.completed** | The number of operations completed by a node. | {operations} | Sum(Int) | | +| elasticsearch.node.operations.get.completed | The number of hits and misses resulting from GET operations. | {operations} | Sum(Int) | | +| elasticsearch.node.operations.get.time | The time spent on hits and misses resulting from GET operations. | ms | Sum(Int) | | | **elasticsearch.node.operations.time** | Time spent on operations by a node. | ms | Sum(Int) | | | **elasticsearch.node.pipeline.ingest.documents.current** | Total number of documents currently being ingested by a pipeline. | {documents} | Sum(Int) | | | **elasticsearch.node.pipeline.ingest.documents.preprocessed** | Number of documents preprocessed by the ingest pipeline. | {documents} | Sum(Int) | | @@ -117,6 +119,7 @@ metrics: | direction | The direction of network data. | received, sent | | document_state (state) | The state of the document. | active, deleted | | fs_direction (direction) | The direction of filesystem IO. | read, write | +| get_result (result) | Result of get operation | hit, miss | | health_status (status) | The health status of the cluster. | green, yellow, red | | index_aggregation_type (aggregation) | Type of shard aggregation for index statistics | primary_shards, total | | indexing_memory_state (state) | State of the indexing memory | current, total | diff --git a/receiver/elasticsearchreceiver/internal/metadata/generated_metrics.go b/receiver/elasticsearchreceiver/internal/metadata/generated_metrics.go index 06305b8121fb..c56008200be4 100644 --- a/receiver/elasticsearchreceiver/internal/metadata/generated_metrics.go +++ b/receiver/elasticsearchreceiver/internal/metadata/generated_metrics.go @@ -82,6 +82,8 @@ type MetricsSettings struct { ElasticsearchNodeIngestOperationsFailed MetricSettings `mapstructure:"elasticsearch.node.ingest.operations.failed"` ElasticsearchNodeOpenFiles MetricSettings `mapstructure:"elasticsearch.node.open_files"` ElasticsearchNodeOperationsCompleted MetricSettings `mapstructure:"elasticsearch.node.operations.completed"` + ElasticsearchNodeOperationsGetCompleted MetricSettings `mapstructure:"elasticsearch.node.operations.get.completed"` + ElasticsearchNodeOperationsGetTime MetricSettings `mapstructure:"elasticsearch.node.operations.get.time"` ElasticsearchNodeOperationsTime MetricSettings `mapstructure:"elasticsearch.node.operations.time"` ElasticsearchNodePipelineIngestDocumentsCurrent MetricSettings `mapstructure:"elasticsearch.node.pipeline.ingest.documents.current"` ElasticsearchNodePipelineIngestDocumentsPreprocessed MetricSettings `mapstructure:"elasticsearch.node.pipeline.ingest.documents.preprocessed"` @@ -253,6 +255,12 @@ func DefaultMetricsSettings() MetricsSettings { ElasticsearchNodeOperationsCompleted: MetricSettings{ Enabled: true, }, + ElasticsearchNodeOperationsGetCompleted: MetricSettings{ + Enabled: false, + }, + ElasticsearchNodeOperationsGetTime: MetricSettings{ + Enabled: false, + }, ElasticsearchNodeOperationsTime: MetricSettings{ Enabled: true, }, @@ -550,6 +558,32 @@ var MapAttributeFsDirection = map[string]AttributeFsDirection{ "write": AttributeFsDirectionWrite, } +// AttributeGetResult specifies the a value get_result attribute. +type AttributeGetResult int + +const ( + _ AttributeGetResult = iota + AttributeGetResultHit + AttributeGetResultMiss +) + +// String returns the string representation of the AttributeGetResult. +func (av AttributeGetResult) String() string { + switch av { + case AttributeGetResultHit: + return "hit" + case AttributeGetResultMiss: + return "miss" + } + return "" +} + +// MapAttributeGetResult is a helper map of string to AttributeGetResult attribute value. +var MapAttributeGetResult = map[string]AttributeGetResult{ + "hit": AttributeGetResultHit, + "miss": AttributeGetResultMiss, +} + // AttributeHealthStatus specifies the a value health_status attribute. type AttributeHealthStatus int @@ -3243,6 +3277,112 @@ func newMetricElasticsearchNodeOperationsCompleted(settings MetricSettings) metr return m } +type metricElasticsearchNodeOperationsGetCompleted struct { + data pmetric.Metric // data buffer for generated metric. + settings MetricSettings // metric settings provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills elasticsearch.node.operations.get.completed metric with initial data. +func (m *metricElasticsearchNodeOperationsGetCompleted) init() { + m.data.SetName("elasticsearch.node.operations.get.completed") + m.data.SetDescription("The number of hits and misses resulting from GET operations.") + m.data.SetUnit("{operations}") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(true) + m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) + m.data.Sum().DataPoints().EnsureCapacity(m.capacity) +} + +func (m *metricElasticsearchNodeOperationsGetCompleted) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, getResultAttributeValue string) { + if !m.settings.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) + dp.Attributes().PutStr("result", getResultAttributeValue) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricElasticsearchNodeOperationsGetCompleted) updateCapacity() { + if m.data.Sum().DataPoints().Len() > m.capacity { + m.capacity = m.data.Sum().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricElasticsearchNodeOperationsGetCompleted) emit(metrics pmetric.MetricSlice) { + if m.settings.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricElasticsearchNodeOperationsGetCompleted(settings MetricSettings) metricElasticsearchNodeOperationsGetCompleted { + m := metricElasticsearchNodeOperationsGetCompleted{settings: settings} + if settings.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricElasticsearchNodeOperationsGetTime struct { + data pmetric.Metric // data buffer for generated metric. + settings MetricSettings // metric settings provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills elasticsearch.node.operations.get.time metric with initial data. +func (m *metricElasticsearchNodeOperationsGetTime) init() { + m.data.SetName("elasticsearch.node.operations.get.time") + m.data.SetDescription("The time spent on hits and misses resulting from GET operations.") + m.data.SetUnit("ms") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(true) + m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) + m.data.Sum().DataPoints().EnsureCapacity(m.capacity) +} + +func (m *metricElasticsearchNodeOperationsGetTime) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, getResultAttributeValue string) { + if !m.settings.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) + dp.Attributes().PutStr("result", getResultAttributeValue) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricElasticsearchNodeOperationsGetTime) updateCapacity() { + if m.data.Sum().DataPoints().Len() > m.capacity { + m.capacity = m.data.Sum().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricElasticsearchNodeOperationsGetTime) emit(metrics pmetric.MetricSlice) { + if m.settings.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricElasticsearchNodeOperationsGetTime(settings MetricSettings) metricElasticsearchNodeOperationsGetTime { + m := metricElasticsearchNodeOperationsGetTime{settings: settings} + if settings.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + type metricElasticsearchNodeOperationsTime struct { data pmetric.Metric // data buffer for generated metric. settings MetricSettings // metric settings provided by user. @@ -4926,6 +5066,8 @@ type MetricsBuilder struct { metricElasticsearchNodeIngestOperationsFailed metricElasticsearchNodeIngestOperationsFailed metricElasticsearchNodeOpenFiles metricElasticsearchNodeOpenFiles metricElasticsearchNodeOperationsCompleted metricElasticsearchNodeOperationsCompleted + metricElasticsearchNodeOperationsGetCompleted metricElasticsearchNodeOperationsGetCompleted + metricElasticsearchNodeOperationsGetTime metricElasticsearchNodeOperationsGetTime metricElasticsearchNodeOperationsTime metricElasticsearchNodeOperationsTime metricElasticsearchNodePipelineIngestDocumentsCurrent metricElasticsearchNodePipelineIngestDocumentsCurrent metricElasticsearchNodePipelineIngestDocumentsPreprocessed metricElasticsearchNodePipelineIngestDocumentsPreprocessed @@ -5020,6 +5162,8 @@ func NewMetricsBuilder(settings MetricsSettings, buildInfo component.BuildInfo, metricElasticsearchNodeIngestOperationsFailed: newMetricElasticsearchNodeIngestOperationsFailed(settings.ElasticsearchNodeIngestOperationsFailed), metricElasticsearchNodeOpenFiles: newMetricElasticsearchNodeOpenFiles(settings.ElasticsearchNodeOpenFiles), metricElasticsearchNodeOperationsCompleted: newMetricElasticsearchNodeOperationsCompleted(settings.ElasticsearchNodeOperationsCompleted), + metricElasticsearchNodeOperationsGetCompleted: newMetricElasticsearchNodeOperationsGetCompleted(settings.ElasticsearchNodeOperationsGetCompleted), + metricElasticsearchNodeOperationsGetTime: newMetricElasticsearchNodeOperationsGetTime(settings.ElasticsearchNodeOperationsGetTime), metricElasticsearchNodeOperationsTime: newMetricElasticsearchNodeOperationsTime(settings.ElasticsearchNodeOperationsTime), metricElasticsearchNodePipelineIngestDocumentsCurrent: newMetricElasticsearchNodePipelineIngestDocumentsCurrent(settings.ElasticsearchNodePipelineIngestDocumentsCurrent), metricElasticsearchNodePipelineIngestDocumentsPreprocessed: newMetricElasticsearchNodePipelineIngestDocumentsPreprocessed(settings.ElasticsearchNodePipelineIngestDocumentsPreprocessed), @@ -5170,6 +5314,8 @@ func (mb *MetricsBuilder) EmitForResource(rmo ...ResourceMetricsOption) { mb.metricElasticsearchNodeIngestOperationsFailed.emit(ils.Metrics()) mb.metricElasticsearchNodeOpenFiles.emit(ils.Metrics()) mb.metricElasticsearchNodeOperationsCompleted.emit(ils.Metrics()) + mb.metricElasticsearchNodeOperationsGetCompleted.emit(ils.Metrics()) + mb.metricElasticsearchNodeOperationsGetTime.emit(ils.Metrics()) mb.metricElasticsearchNodeOperationsTime.emit(ils.Metrics()) mb.metricElasticsearchNodePipelineIngestDocumentsCurrent.emit(ils.Metrics()) mb.metricElasticsearchNodePipelineIngestDocumentsPreprocessed.emit(ils.Metrics()) @@ -5446,6 +5592,16 @@ func (mb *MetricsBuilder) RecordElasticsearchNodeOperationsCompletedDataPoint(ts mb.metricElasticsearchNodeOperationsCompleted.recordDataPoint(mb.startTime, ts, val, operationAttributeValue.String()) } +// RecordElasticsearchNodeOperationsGetCompletedDataPoint adds a data point to elasticsearch.node.operations.get.completed metric. +func (mb *MetricsBuilder) RecordElasticsearchNodeOperationsGetCompletedDataPoint(ts pcommon.Timestamp, val int64, getResultAttributeValue AttributeGetResult) { + mb.metricElasticsearchNodeOperationsGetCompleted.recordDataPoint(mb.startTime, ts, val, getResultAttributeValue.String()) +} + +// RecordElasticsearchNodeOperationsGetTimeDataPoint adds a data point to elasticsearch.node.operations.get.time metric. +func (mb *MetricsBuilder) RecordElasticsearchNodeOperationsGetTimeDataPoint(ts pcommon.Timestamp, val int64, getResultAttributeValue AttributeGetResult) { + mb.metricElasticsearchNodeOperationsGetTime.recordDataPoint(mb.startTime, ts, val, getResultAttributeValue.String()) +} + // RecordElasticsearchNodeOperationsTimeDataPoint adds a data point to elasticsearch.node.operations.time metric. func (mb *MetricsBuilder) RecordElasticsearchNodeOperationsTimeDataPoint(ts pcommon.Timestamp, val int64, operationAttributeValue AttributeOperation) { mb.metricElasticsearchNodeOperationsTime.recordDataPoint(mb.startTime, ts, val, operationAttributeValue.String()) diff --git a/receiver/elasticsearchreceiver/internal/model/nodestats.go b/receiver/elasticsearchreceiver/internal/model/nodestats.go index 31ad85d2f92a..c2923eaede37 100644 --- a/receiver/elasticsearchreceiver/internal/model/nodestats.go +++ b/receiver/elasticsearchreceiver/internal/model/nodestats.go @@ -219,8 +219,12 @@ type IndexingOperations struct { } type GetOperation struct { - Total int64 `json:"total"` - TotalTimeInMs int64 `json:"time_in_millis"` + Total int64 `json:"total"` + TotalTimeInMs int64 `json:"time_in_millis"` + Exists int64 `json:"exists_total"` + ExistsTimeInMs int64 `json:"exists_time_in_millis"` + Missing int64 `json:"missing_total"` + MissingTimeInMs int64 `json:"missing_time_in_millis"` } type SearchOperations struct { diff --git a/receiver/elasticsearchreceiver/metadata.yaml b/receiver/elasticsearchreceiver/metadata.yaml index 606f6861d304..7533fb1b0b27 100644 --- a/receiver/elasticsearchreceiver/metadata.yaml +++ b/receiver/elasticsearchreceiver/metadata.yaml @@ -154,6 +154,12 @@ attributes: - doc_value - index_writer - fixed_bit_set + get_result: + value: result + description: Result of get operation + enum: + - hit + - miss metrics: # these metrics are from /_nodes/stats, and are node level metrics @@ -309,6 +315,24 @@ metrics: value_type: int attributes: [operation] enabled: true + elasticsearch.node.operations.get.completed: + description: The number of hits and misses resulting from GET operations. + unit: "{operations}" + sum: + monotonic: true + aggregation: cumulative + value_type: int + attributes: [get_result] + enabled: false + elasticsearch.node.operations.get.time: + description: The time spent on hits and misses resulting from GET operations. + unit: ms + sum: + monotonic: true + aggregation: cumulative + value_type: int + attributes: [get_result] + enabled: false elasticsearch.node.shards.size: description: The size of the shards assigned to this node. unit: By diff --git a/receiver/elasticsearchreceiver/scraper.go b/receiver/elasticsearchreceiver/scraper.go index 562381c29650..066d0de11326 100644 --- a/receiver/elasticsearchreceiver/scraper.go +++ b/receiver/elasticsearchreceiver/scraper.go @@ -160,6 +160,12 @@ func (r *elasticsearchScraper) scrapeNodeMetrics(ctx context.Context, now pcommo r.mb.RecordElasticsearchNodeOperationsTimeDataPoint(now, info.Indices.FlushOperations.TotalTimeInMs, metadata.AttributeOperationFlush) r.mb.RecordElasticsearchNodeOperationsTimeDataPoint(now, info.Indices.WarmerOperations.TotalTimeInMs, metadata.AttributeOperationWarmer) + r.mb.RecordElasticsearchNodeOperationsGetCompletedDataPoint(now, info.Indices.GetOperation.Exists, metadata.AttributeGetResultHit) + r.mb.RecordElasticsearchNodeOperationsGetCompletedDataPoint(now, info.Indices.GetOperation.Missing, metadata.AttributeGetResultMiss) + + r.mb.RecordElasticsearchNodeOperationsGetTimeDataPoint(now, info.Indices.GetOperation.ExistsTimeInMs, metadata.AttributeGetResultHit) + r.mb.RecordElasticsearchNodeOperationsGetTimeDataPoint(now, info.Indices.GetOperation.MissingTimeInMs, metadata.AttributeGetResultMiss) + r.mb.RecordElasticsearchNodeShardsSizeDataPoint(now, info.Indices.StoreInfo.SizeInBy) // Elasticsearch version 7.13+ is required to collect `elasticsearch.node.shards.data_set.size`. diff --git a/receiver/elasticsearchreceiver/scraper_test.go b/receiver/elasticsearchreceiver/scraper_test.go index b0613c35ac5a..d042911937b4 100644 --- a/receiver/elasticsearchreceiver/scraper_test.go +++ b/receiver/elasticsearchreceiver/scraper_test.go @@ -42,6 +42,10 @@ func TestScraper(t *testing.T) { t.Parallel() config := createDefaultConfig().(*Config) + + config.Metrics.ElasticsearchNodeOperationsGetCompleted.Enabled = true + config.Metrics.ElasticsearchNodeOperationsGetTime.Enabled = true + config.Metrics.ElasticsearchIndexOperationsMergeSize.Enabled = true config.Metrics.ElasticsearchIndexOperationsMergeDocsCount.Enabled = true config.Metrics.ElasticsearchIndexSegmentsCount.Enabled = true diff --git a/receiver/elasticsearchreceiver/testdata/expected_metrics/full.json b/receiver/elasticsearchreceiver/testdata/expected_metrics/full.json index 78dcd37e4253..8bf38acd241c 100644 --- a/receiver/elasticsearchreceiver/testdata/expected_metrics/full.json +++ b/receiver/elasticsearchreceiver/testdata/expected_metrics/full.json @@ -1450,6 +1450,80 @@ }, "unit": "ms" }, + { + "description": "The number of hits and misses resulting from GET operations.", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "512", + "attributes": [ + { + "key": "result", + "value": { + "stringValue": "hit" + } + } + ], + "startTimeUnixNano": "1661811026803971000", + "timeUnixNano": "1661811026805343000" + }, + { + "asInt": "512", + "attributes": [ + { + "key": "result", + "value": { + "stringValue": "miss" + } + } + ], + "startTimeUnixNano": "1661811026803971000", + "timeUnixNano": "1661811026805343000" + } + ], + "isMonotonic": true + }, + "name": "elasticsearch.node.operations.get.completed", + "unit": "{operations}" + }, + { + "description": "The time spent on hits and misses resulting from GET operations.", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "209", + "attributes": [ + { + "key": "result", + "value": { + "stringValue": "hit" + } + } + ], + "startTimeUnixNano": "1661811026803971000", + "timeUnixNano": "1661811026805343000" + }, + { + "asInt": "124", + "attributes": [ + { + "key": "result", + "value": { + "stringValue": "miss" + } + } + ], + "startTimeUnixNano": "1661811026803971000", + "timeUnixNano": "1661811026805343000" + } + ], + "isMonotonic": true + }, + "name": "elasticsearch.node.operations.get.time", + "unit": "ms" + }, { "description": "Total number of documents currently being ingested by a pipeline.", "name": "elasticsearch.node.pipeline.ingest.documents.current",