From b72951bb7b0a3f765510e81db1959e0688488900 Mon Sep 17 00:00:00 2001 From: Dominik Rosiek <58699848+sumo-drosiek@users.noreply.github.com> Date: Tue, 17 Jan 2023 18:01:57 +0100 Subject: [PATCH] [receiver/mysql]: add mysql.commands metric with support for delete, insert, select, update (#17589) feat(mysqlreceiver): add mysql.commands metric with support for delete, insert, select, update Signed-off-by: Dominik Rosiek --- .chloggen/drosiek-mysql-commands.yaml | 16 +++ receiver/mysqlreceiver/documentation.md | 14 +++ .../internal/metadata/generated_metrics.go | 104 ++++++++++++++++++ .../metadata/generated_metrics_test.go | 20 ++++ .../internal/metadata/testdata/config.yaml | 4 + receiver/mysqlreceiver/metadata.yaml | 14 +++ receiver/mysqlreceiver/scraper.go | 12 +- receiver/mysqlreceiver/scraper_test.go | 1 + .../testdata/scraper/expected.json | 63 +++++++++++ 9 files changed, 247 insertions(+), 1 deletion(-) create mode 100755 .chloggen/drosiek-mysql-commands.yaml diff --git a/.chloggen/drosiek-mysql-commands.yaml b/.chloggen/drosiek-mysql-commands.yaml new file mode 100755 index 000000000000..f06dc95ffc31 --- /dev/null +++ b/.chloggen/drosiek-mysql-commands.yaml @@ -0,0 +1,16 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: mysqlreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: add mysql.commands metric with supprot for delete, insert, select, update + +# One or more tracking issues related to the change +issues: [14138] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: \ No newline at end of file diff --git a/receiver/mysqlreceiver/documentation.md b/receiver/mysqlreceiver/documentation.md index cd700327f61b..f0715815eb13 100644 --- a/receiver/mysqlreceiver/documentation.md +++ b/receiver/mysqlreceiver/documentation.md @@ -380,6 +380,20 @@ The number of transmitted bytes between server and clients. | ---- | ----------- | ------ | | kind | The name of the transmission direction. | Str: ``received``, ``sent`` | +### mysql.commands + +The number of times each type of command has been executed. + +| Unit | Metric Type | Value Type | Aggregation Temporality | Monotonic | +| ---- | ----------- | ---------- | ----------------------- | --------- | +| 1 | Sum | Int | Cumulative | true | + +#### Attributes + +| Name | Description | Values | +| ---- | ----------- | ------ | +| command | The command types. | Str: ``delete``, ``insert``, ``select``, ``update`` | + ### mysql.connection.count The number of connection attempts (successful or not) to the MySQL server. diff --git a/receiver/mysqlreceiver/internal/metadata/generated_metrics.go b/receiver/mysqlreceiver/internal/metadata/generated_metrics.go index ddc26f27e033..a63fd0f03d69 100644 --- a/receiver/mysqlreceiver/internal/metadata/generated_metrics.go +++ b/receiver/mysqlreceiver/internal/metadata/generated_metrics.go @@ -42,6 +42,7 @@ type MetricsSettings struct { MysqlBufferPoolPages MetricSettings `mapstructure:"mysql.buffer_pool.pages"` MysqlBufferPoolUsage MetricSettings `mapstructure:"mysql.buffer_pool.usage"` MysqlClientNetworkIo MetricSettings `mapstructure:"mysql.client.network.io"` + MysqlCommands MetricSettings `mapstructure:"mysql.commands"` MysqlConnectionCount MetricSettings `mapstructure:"mysql.connection.count"` MysqlConnectionErrors MetricSettings `mapstructure:"mysql.connection.errors"` MysqlDoubleWrites MetricSettings `mapstructure:"mysql.double_writes"` @@ -102,6 +103,9 @@ func DefaultMetricsSettings() MetricsSettings { MysqlClientNetworkIo: MetricSettings{ Enabled: false, }, + MysqlCommands: MetricSettings{ + Enabled: false, + }, MysqlConnectionCount: MetricSettings{ Enabled: false, }, @@ -342,6 +346,40 @@ var MapAttributeCacheStatus = map[string]AttributeCacheStatus{ "overflow": AttributeCacheStatusOverflow, } +// AttributeCommand specifies the a value command attribute. +type AttributeCommand int + +const ( + _ AttributeCommand = iota + AttributeCommandDelete + AttributeCommandInsert + AttributeCommandSelect + AttributeCommandUpdate +) + +// String returns the string representation of the AttributeCommand. +func (av AttributeCommand) String() string { + switch av { + case AttributeCommandDelete: + return "delete" + case AttributeCommandInsert: + return "insert" + case AttributeCommandSelect: + return "select" + case AttributeCommandUpdate: + return "update" + } + return "" +} + +// MapAttributeCommand is a helper map of string to AttributeCommand attribute value. +var MapAttributeCommand = map[string]AttributeCommand{ + "delete": AttributeCommandDelete, + "insert": AttributeCommandInsert, + "select": AttributeCommandSelect, + "update": AttributeCommandUpdate, +} + // AttributeConnectionError specifies the a value connection_error attribute. type AttributeConnectionError int @@ -1501,6 +1539,59 @@ func newMetricMysqlClientNetworkIo(settings MetricSettings) metricMysqlClientNet return m } +type metricMysqlCommands 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 mysql.commands metric with initial data. +func (m *metricMysqlCommands) init() { + m.data.SetName("mysql.commands") + m.data.SetDescription("The number of times each type of command has been executed.") + m.data.SetUnit("1") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(true) + m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) + m.data.Sum().DataPoints().EnsureCapacity(m.capacity) +} + +func (m *metricMysqlCommands) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, commandAttributeValue string) { + if !m.settings.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) + dp.Attributes().PutStr("command", commandAttributeValue) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricMysqlCommands) 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 *metricMysqlCommands) emit(metrics pmetric.MetricSlice) { + if m.settings.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricMysqlCommands(settings MetricSettings) metricMysqlCommands { + m := metricMysqlCommands{settings: settings} + if settings.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + type metricMysqlConnectionCount struct { data pmetric.Metric // data buffer for generated metric. settings MetricSettings // metric settings provided by user. @@ -3380,6 +3471,7 @@ type MetricsBuilder struct { metricMysqlBufferPoolPages metricMysqlBufferPoolPages metricMysqlBufferPoolUsage metricMysqlBufferPoolUsage metricMysqlClientNetworkIo metricMysqlClientNetworkIo + metricMysqlCommands metricMysqlCommands metricMysqlConnectionCount metricMysqlConnectionCount metricMysqlConnectionErrors metricMysqlConnectionErrors metricMysqlDoubleWrites metricMysqlDoubleWrites @@ -3439,6 +3531,7 @@ func NewMetricsBuilder(ms MetricsSettings, settings receiver.CreateSettings, opt metricMysqlBufferPoolPages: newMetricMysqlBufferPoolPages(ms.MysqlBufferPoolPages), metricMysqlBufferPoolUsage: newMetricMysqlBufferPoolUsage(ms.MysqlBufferPoolUsage), metricMysqlClientNetworkIo: newMetricMysqlClientNetworkIo(ms.MysqlClientNetworkIo), + metricMysqlCommands: newMetricMysqlCommands(ms.MysqlCommands), metricMysqlConnectionCount: newMetricMysqlConnectionCount(ms.MysqlConnectionCount), metricMysqlConnectionErrors: newMetricMysqlConnectionErrors(ms.MysqlConnectionErrors), metricMysqlDoubleWrites: newMetricMysqlDoubleWrites(ms.MysqlDoubleWrites), @@ -3540,6 +3633,7 @@ func (mb *MetricsBuilder) EmitForResource(rmo ...ResourceMetricsOption) { mb.metricMysqlBufferPoolPages.emit(ils.Metrics()) mb.metricMysqlBufferPoolUsage.emit(ils.Metrics()) mb.metricMysqlClientNetworkIo.emit(ils.Metrics()) + mb.metricMysqlCommands.emit(ils.Metrics()) mb.metricMysqlConnectionCount.emit(ils.Metrics()) mb.metricMysqlConnectionErrors.emit(ils.Metrics()) mb.metricMysqlDoubleWrites.emit(ils.Metrics()) @@ -3654,6 +3748,16 @@ func (mb *MetricsBuilder) RecordMysqlClientNetworkIoDataPoint(ts pcommon.Timesta return nil } +// RecordMysqlCommandsDataPoint adds a data point to mysql.commands metric. +func (mb *MetricsBuilder) RecordMysqlCommandsDataPoint(ts pcommon.Timestamp, inputVal string, commandAttributeValue AttributeCommand) error { + val, err := strconv.ParseInt(inputVal, 10, 64) + if err != nil { + return fmt.Errorf("failed to parse int64 for MysqlCommands, value was %s: %w", inputVal, err) + } + mb.metricMysqlCommands.recordDataPoint(mb.startTime, ts, val, commandAttributeValue.String()) + return nil +} + // RecordMysqlConnectionCountDataPoint adds a data point to mysql.connection.count metric. func (mb *MetricsBuilder) RecordMysqlConnectionCountDataPoint(ts pcommon.Timestamp, inputVal string) error { val, err := strconv.ParseInt(inputVal, 10, 64) diff --git a/receiver/mysqlreceiver/internal/metadata/generated_metrics_test.go b/receiver/mysqlreceiver/internal/metadata/generated_metrics_test.go index 113bac7ba1a2..f90b34779358 100644 --- a/receiver/mysqlreceiver/internal/metadata/generated_metrics_test.go +++ b/receiver/mysqlreceiver/internal/metadata/generated_metrics_test.go @@ -85,6 +85,9 @@ func TestMetricsBuilder(t *testing.T) { allMetricsCount++ mb.RecordMysqlClientNetworkIoDataPoint(ts, "1", AttributeDirection(1)) + allMetricsCount++ + mb.RecordMysqlCommandsDataPoint(ts, "1", AttributeCommand(1)) + allMetricsCount++ mb.RecordMysqlConnectionCountDataPoint(ts, "1") @@ -349,6 +352,23 @@ func TestMetricsBuilder(t *testing.T) { attrVal, ok := dp.Attributes().Get("kind") assert.True(t, ok) assert.Equal(t, "received", attrVal.Str()) + case "mysql.commands": + assert.False(t, validatedMetrics["mysql.commands"], "Found a duplicate in the metrics slice: mysql.commands") + validatedMetrics["mysql.commands"] = true + assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, "The number of times each type of command has been executed.", ms.At(i).Description()) + assert.Equal(t, "1", ms.At(i).Unit()) + assert.Equal(t, true, ms.At(i).Sum().IsMonotonic()) + assert.Equal(t, pmetric.AggregationTemporalityCumulative, ms.At(i).Sum().AggregationTemporality()) + dp := ms.At(i).Sum().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) + assert.Equal(t, int64(1), dp.IntValue()) + attrVal, ok := dp.Attributes().Get("command") + assert.True(t, ok) + assert.Equal(t, "delete", attrVal.Str()) case "mysql.connection.count": assert.False(t, validatedMetrics["mysql.connection.count"], "Found a duplicate in the metrics slice: mysql.connection.count") validatedMetrics["mysql.connection.count"] = true diff --git a/receiver/mysqlreceiver/internal/metadata/testdata/config.yaml b/receiver/mysqlreceiver/internal/metadata/testdata/config.yaml index a7f038f5ea30..c84485ba3eb5 100644 --- a/receiver/mysqlreceiver/internal/metadata/testdata/config.yaml +++ b/receiver/mysqlreceiver/internal/metadata/testdata/config.yaml @@ -14,6 +14,8 @@ all_metrics: enabled: true mysql.client.network.io: enabled: true + mysql.commands: + enabled: true mysql.connection.count: enabled: true mysql.connection.errors: @@ -99,6 +101,8 @@ no_metrics: enabled: false mysql.client.network.io: enabled: false + mysql.commands: + enabled: false mysql.connection.count: enabled: false mysql.connection.errors: diff --git a/receiver/mysqlreceiver/metadata.yaml b/receiver/mysqlreceiver/metadata.yaml index c45caf1e42eb..189a5be808fc 100644 --- a/receiver/mysqlreceiver/metadata.yaml +++ b/receiver/mysqlreceiver/metadata.yaml @@ -26,6 +26,10 @@ attributes: description: The prepare statement command types. type: string enum: [execute, close, fetch, prepare, reset, send_long_data] + command: + description: The command types. + type: string + enum: [delete, insert, select, update] connection_error: name_override: error description: The connection error type. @@ -221,6 +225,16 @@ metrics: monotonic: true aggregation: cumulative attributes: [prepared_statements_command] + mysql.commands: + enabled: false + description: The number of times each type of command has been executed. + unit: 1 + sum: + value_type: int + input_type: string + monotonic: true + aggregation: cumulative + attributes: [command] mysql.handlers: enabled: true description: The number of requests to various MySQL handlers. diff --git a/receiver/mysqlreceiver/scraper.go b/receiver/mysqlreceiver/scraper.go index 9189b00bec0a..7a1d079a8dec 100644 --- a/receiver/mysqlreceiver/scraper.go +++ b/receiver/mysqlreceiver/scraper.go @@ -201,7 +201,7 @@ func (m *mySQLScraper) scrapeGlobalStats(now pcommon.Timestamp, errs *scrapererr case "Connections": addPartialIfError(errs, m.mb.RecordMysqlConnectionCountDataPoint(now, v)) - // commands + // prepared_statements_commands case "Com_stmt_execute": addPartialIfError(errs, m.mb.RecordMysqlPreparedStatementsDataPoint(now, v, metadata.AttributePreparedStatementsCommandExecute)) @@ -221,6 +221,16 @@ func (m *mySQLScraper) scrapeGlobalStats(now pcommon.Timestamp, errs *scrapererr addPartialIfError(errs, m.mb.RecordMysqlPreparedStatementsDataPoint(now, v, metadata.AttributePreparedStatementsCommandSendLongData)) + // commands + case "Com_delete": + addPartialIfError(errs, m.mb.RecordMysqlCommandsDataPoint(now, v, metadata.AttributeCommandDelete)) + case "Com_insert": + addPartialIfError(errs, m.mb.RecordMysqlCommandsDataPoint(now, v, metadata.AttributeCommandInsert)) + case "Com_select": + addPartialIfError(errs, m.mb.RecordMysqlCommandsDataPoint(now, v, metadata.AttributeCommandSelect)) + case "Com_update": + addPartialIfError(errs, m.mb.RecordMysqlCommandsDataPoint(now, v, metadata.AttributeCommandUpdate)) + // created tmps case "Created_tmp_disk_tables": addPartialIfError(errs, m.mb.RecordMysqlTmpResourcesDataPoint(now, v, metadata.AttributeTmpResourceDiskTables)) diff --git a/receiver/mysqlreceiver/scraper_test.go b/receiver/mysqlreceiver/scraper_test.go index 595b9e97761d..46243745e248 100644 --- a/receiver/mysqlreceiver/scraper_test.go +++ b/receiver/mysqlreceiver/scraper_test.go @@ -57,6 +57,7 @@ func TestScrape(t *testing.T) { cfg.Metrics.MysqlClientNetworkIo.Enabled = true cfg.Metrics.MysqlPreparedStatements.Enabled = true + cfg.Metrics.MysqlCommands.Enabled = true cfg.Metrics.MysqlReplicaSQLDelay.Enabled = true cfg.Metrics.MysqlReplicaTimeBehindSource.Enabled = true diff --git a/receiver/mysqlreceiver/testdata/scraper/expected.json b/receiver/mysqlreceiver/testdata/scraper/expected.json index 7204ec7e6939..6d2f20d02930 100644 --- a/receiver/mysqlreceiver/testdata/scraper/expected.json +++ b/receiver/mysqlreceiver/testdata/scraper/expected.json @@ -2976,6 +2976,69 @@ ] }, "unit": "s" + }, + { + "description": "The number of times each type of command has been executed.", + "name": "mysql.commands", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "50", + "attributes": [ + { + "key": "command", + "value": { + "stringValue": "delete" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "78", + "attributes": [ + { + "key": "command", + "value": { + "stringValue": "insert" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "106", + "attributes": [ + { + "key": "command", + "value": { + "stringValue": "select" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "173", + "attributes": [ + { + "key": "command", + "value": { + "stringValue": "update" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + } + ], + "isMonotonic": true + }, + "unit": "1" } ] }