From e2f8c34e93a47cdec88541a21f0a05802803db5c Mon Sep 17 00:00:00 2001 From: Dominik Rosiek Date: Wed, 5 Oct 2022 11:04:19 +0200 Subject: [PATCH] feat(mysqlreceiver): Add mysql.connection.errors metric Signed-off-by: Dominik Rosiek --- .../drosiek-mysql-connection-errors.yaml | 16 +++ receiver/mysqlreceiver/documentation.md | 2 + .../internal/metadata/generated_metrics.go | 112 ++++++++++++++++++ receiver/mysqlreceiver/metadata.yaml | 14 +++ receiver/mysqlreceiver/scraper.go | 20 ++++ .../testdata/scraper/expected.json | 89 ++++++++++++++ 6 files changed, 253 insertions(+) create mode 100755 .chloggen/drosiek-mysql-connection-errors.yaml diff --git a/.chloggen/drosiek-mysql-connection-errors.yaml b/.chloggen/drosiek-mysql-connection-errors.yaml new file mode 100755 index 000000000000..837aab621015 --- /dev/null +++ b/.chloggen/drosiek-mysql-connection-errors.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.connection.errors metric + +# 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: diff --git a/receiver/mysqlreceiver/documentation.md b/receiver/mysqlreceiver/documentation.md index 9b35170ee455..7997bda90be3 100644 --- a/receiver/mysqlreceiver/documentation.md +++ b/receiver/mysqlreceiver/documentation.md @@ -15,6 +15,7 @@ These are the metrics available for this scraper. | **mysql.buffer_pool.pages** | The number of pages in the InnoDB buffer pool. | 1 | Sum(Int) | | | **mysql.buffer_pool.usage** | The number of bytes in the InnoDB buffer pool. | By | Sum(Int) | | | **mysql.commands** | The number of times each type of command has been executed. | 1 | Sum(Int) | | +| **mysql.connection.errors** | Errors that occur during the client connection process. | 1 | Sum(Int) | | | **mysql.double_writes** | The number of writes to the InnoDB doublewrite buffer. | 1 | Sum(Int) | | | **mysql.handlers** | The number of requests to various MySQL handlers. | 1 | Sum(Int) | | | **mysql.index.io.wait.count** | The total count of I/O wait events for an index. | 1 | Sum(Int) | | @@ -54,6 +55,7 @@ metrics: | buffer_pool_operations (operation) | The buffer pool operations types. | read_ahead_rnd, read_ahead, read_ahead_evicted, read_requests, reads, wait_free, write_requests | | buffer_pool_pages (kind) | The buffer pool pages types. | data, free, misc | | command (command) | The command types. | execute, close, fetch, prepare, reset, send_long_data | +| connection_error (error) | The connection error type. | accept, internal, max_connections, peer_address, select, tcpwrap | | double_writes (kind) | The doublewrite types. | pages_written, writes | | handler (kind) | The handler types. | commit, delete, discover, external_lock, mrr_init, prepare, read_first, read_key, read_last, read_next, read_prev, read_rnd, read_rnd_next, rollback, savepoint, savepoint_rollback, update, write | | index_name (index) | The name of the index. | | diff --git a/receiver/mysqlreceiver/internal/metadata/generated_metrics.go b/receiver/mysqlreceiver/internal/metadata/generated_metrics.go index bd607f400d3c..e3e2325d6b16 100644 --- a/receiver/mysqlreceiver/internal/metadata/generated_metrics.go +++ b/receiver/mysqlreceiver/internal/metadata/generated_metrics.go @@ -26,6 +26,7 @@ type MetricsSettings struct { MysqlBufferPoolPages MetricSettings `mapstructure:"mysql.buffer_pool.pages"` MysqlBufferPoolUsage MetricSettings `mapstructure:"mysql.buffer_pool.usage"` MysqlCommands MetricSettings `mapstructure:"mysql.commands"` + MysqlConnectionErrors MetricSettings `mapstructure:"mysql.connection.errors"` MysqlDoubleWrites MetricSettings `mapstructure:"mysql.double_writes"` MysqlHandlers MetricSettings `mapstructure:"mysql.handlers"` MysqlIndexIoWaitCount MetricSettings `mapstructure:"mysql.index.io.wait.count"` @@ -66,6 +67,9 @@ func DefaultMetricsSettings() MetricsSettings { MysqlCommands: MetricSettings{ Enabled: true, }, + MysqlConnectionErrors: MetricSettings{ + Enabled: true, + }, MysqlDoubleWrites: MetricSettings{ Enabled: true, }, @@ -258,6 +262,48 @@ var MapAttributeCommand = map[string]AttributeCommand{ "send_long_data": AttributeCommandSendLongData, } +// AttributeConnectionError specifies the a value connection_error attribute. +type AttributeConnectionError int + +const ( + _ AttributeConnectionError = iota + AttributeConnectionErrorAccept + AttributeConnectionErrorInternal + AttributeConnectionErrorMaxConnections + AttributeConnectionErrorPeerAddress + AttributeConnectionErrorSelect + AttributeConnectionErrorTcpwrap +) + +// String returns the string representation of the AttributeConnectionError. +func (av AttributeConnectionError) String() string { + switch av { + case AttributeConnectionErrorAccept: + return "accept" + case AttributeConnectionErrorInternal: + return "internal" + case AttributeConnectionErrorMaxConnections: + return "max_connections" + case AttributeConnectionErrorPeerAddress: + return "peer_address" + case AttributeConnectionErrorSelect: + return "select" + case AttributeConnectionErrorTcpwrap: + return "tcpwrap" + } + return "" +} + +// MapAttributeConnectionError is a helper map of string to AttributeConnectionError attribute value. +var MapAttributeConnectionError = map[string]AttributeConnectionError{ + "accept": AttributeConnectionErrorAccept, + "internal": AttributeConnectionErrorInternal, + "max_connections": AttributeConnectionErrorMaxConnections, + "peer_address": AttributeConnectionErrorPeerAddress, + "select": AttributeConnectionErrorSelect, + "tcpwrap": AttributeConnectionErrorTcpwrap, +} + // AttributeDoubleWrites specifies the a value double_writes attribute. type AttributeDoubleWrites int @@ -1049,6 +1095,59 @@ func newMetricMysqlCommands(settings MetricSettings) metricMysqlCommands { return m } +type metricMysqlConnectionErrors 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.connection.errors metric with initial data. +func (m *metricMysqlConnectionErrors) init() { + m.data.SetName("mysql.connection.errors") + m.data.SetDescription("Errors that occur during the client connection process.") + m.data.SetUnit("1") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(true) + m.data.Sum().SetAggregationTemporality(pmetric.MetricAggregationTemporalityCumulative) + m.data.Sum().DataPoints().EnsureCapacity(m.capacity) +} + +func (m *metricMysqlConnectionErrors) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, connectionErrorAttributeValue string) { + if !m.settings.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) + dp.Attributes().PutStr("error", connectionErrorAttributeValue) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricMysqlConnectionErrors) 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 *metricMysqlConnectionErrors) emit(metrics pmetric.MetricSlice) { + if m.settings.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricMysqlConnectionErrors(settings MetricSettings) metricMysqlConnectionErrors { + m := metricMysqlConnectionErrors{settings: settings} + if settings.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + type metricMysqlDoubleWrites struct { data pmetric.Metric // data buffer for generated metric. settings MetricSettings // metric settings provided by user. @@ -1869,6 +1968,7 @@ type MetricsBuilder struct { metricMysqlBufferPoolPages metricMysqlBufferPoolPages metricMysqlBufferPoolUsage metricMysqlBufferPoolUsage metricMysqlCommands metricMysqlCommands + metricMysqlConnectionErrors metricMysqlConnectionErrors metricMysqlDoubleWrites metricMysqlDoubleWrites metricMysqlHandlers metricMysqlHandlers metricMysqlIndexIoWaitCount metricMysqlIndexIoWaitCount @@ -1908,6 +2008,7 @@ func NewMetricsBuilder(settings MetricsSettings, buildInfo component.BuildInfo, metricMysqlBufferPoolPages: newMetricMysqlBufferPoolPages(settings.MysqlBufferPoolPages), metricMysqlBufferPoolUsage: newMetricMysqlBufferPoolUsage(settings.MysqlBufferPoolUsage), metricMysqlCommands: newMetricMysqlCommands(settings.MysqlCommands), + metricMysqlConnectionErrors: newMetricMysqlConnectionErrors(settings.MysqlConnectionErrors), metricMysqlDoubleWrites: newMetricMysqlDoubleWrites(settings.MysqlDoubleWrites), metricMysqlHandlers: newMetricMysqlHandlers(settings.MysqlHandlers), metricMysqlIndexIoWaitCount: newMetricMysqlIndexIoWaitCount(settings.MysqlIndexIoWaitCount), @@ -1989,6 +2090,7 @@ func (mb *MetricsBuilder) EmitForResource(rmo ...ResourceMetricsOption) { mb.metricMysqlBufferPoolPages.emit(ils.Metrics()) mb.metricMysqlBufferPoolUsage.emit(ils.Metrics()) mb.metricMysqlCommands.emit(ils.Metrics()) + mb.metricMysqlConnectionErrors.emit(ils.Metrics()) mb.metricMysqlDoubleWrites.emit(ils.Metrics()) mb.metricMysqlHandlers.emit(ils.Metrics()) mb.metricMysqlIndexIoWaitCount.emit(ils.Metrics()) @@ -2083,6 +2185,16 @@ func (mb *MetricsBuilder) RecordMysqlCommandsDataPoint(ts pcommon.Timestamp, inp return nil } +// RecordMysqlConnectionErrorsDataPoint adds a data point to mysql.connection.errors metric. +func (mb *MetricsBuilder) RecordMysqlConnectionErrorsDataPoint(ts pcommon.Timestamp, inputVal string, connectionErrorAttributeValue AttributeConnectionError) error { + val, err := strconv.ParseInt(inputVal, 10, 64) + if err != nil { + return fmt.Errorf("failed to parse int64 for MysqlConnectionErrors, value was %s: %w", inputVal, err) + } + mb.metricMysqlConnectionErrors.recordDataPoint(mb.startTime, ts, val, connectionErrorAttributeValue.String()) + return nil +} + // RecordMysqlDoubleWritesDataPoint adds a data point to mysql.double_writes metric. func (mb *MetricsBuilder) RecordMysqlDoubleWritesDataPoint(ts pcommon.Timestamp, inputVal string, doubleWritesAttributeValue AttributeDoubleWrites) error { val, err := strconv.ParseInt(inputVal, 10, 64) diff --git a/receiver/mysqlreceiver/metadata.yaml b/receiver/mysqlreceiver/metadata.yaml index 8d919bdafd28..f106027f9c86 100644 --- a/receiver/mysqlreceiver/metadata.yaml +++ b/receiver/mysqlreceiver/metadata.yaml @@ -22,6 +22,10 @@ attributes: value: command description: The command types. enum: [execute, close, fetch, prepare, reset, send_long_data] + connection_error: + value: error + description: The connection error type. + enum: [accept, internal, max_connections, peer_address, select, tcpwrap] handler: value: kind description: The handler types. @@ -283,6 +287,16 @@ metrics: monotonic: false aggregation: cumulative attributes: [threads] + mysql.connection.errors: + enabled: true + description: Errors that occur during the client connection process. + unit: 1 + sum: + value_type: int + input_type: string + monotonic: true + aggregation: cumulative + attributes: [connection_error] mysql.tmp_resources: enabled: true description: The number of created temporary resources. diff --git a/receiver/mysqlreceiver/scraper.go b/receiver/mysqlreceiver/scraper.go index 9b7163505547..c0c1e2c7f8e5 100644 --- a/receiver/mysqlreceiver/scraper.go +++ b/receiver/mysqlreceiver/scraper.go @@ -158,6 +158,26 @@ func (m *mySQLScraper) scrapeGlobalStats(now pcommon.Timestamp, errs *scrapererr addPartialIfError(errs, m.mb.RecordMysqlBufferPoolOperationsDataPoint(now, v, metadata.AttributeBufferPoolOperationsWriteRequests)) + // connection.errors + case "Connection_errors_accept": + addPartialIfError(errs, m.mb.RecordMysqlConnectionErrorsDataPoint(now, v, + metadata.AttributeConnectionErrorAccept)) + case "Connection_errors_internal": + addPartialIfError(errs, m.mb.RecordMysqlConnectionErrorsDataPoint(now, v, + metadata.AttributeConnectionErrorInternal)) + case "Connection_errors_max_connections": + addPartialIfError(errs, m.mb.RecordMysqlConnectionErrorsDataPoint(now, v, + metadata.AttributeConnectionErrorMaxConnections)) + case "Connection_errors_peer_address": + addPartialIfError(errs, m.mb.RecordMysqlConnectionErrorsDataPoint(now, v, + metadata.AttributeConnectionErrorPeerAddress)) + case "Connection_errors_select": + addPartialIfError(errs, m.mb.RecordMysqlConnectionErrorsDataPoint(now, v, + metadata.AttributeConnectionErrorSelect)) + case "Connection_errors_tcpwrap": + addPartialIfError(errs, m.mb.RecordMysqlConnectionErrorsDataPoint(now, v, + metadata.AttributeConnectionErrorTcpwrap)) + // commands case "Com_stmt_execute": addPartialIfError(errs, m.mb.RecordMysqlCommandsDataPoint(now, v, metadata.AttributeCommandExecute)) diff --git a/receiver/mysqlreceiver/testdata/scraper/expected.json b/receiver/mysqlreceiver/testdata/scraper/expected.json index bed533c47f10..508907bd34c1 100644 --- a/receiver/mysqlreceiver/testdata/scraper/expected.json +++ b/receiver/mysqlreceiver/testdata/scraper/expected.json @@ -351,6 +351,95 @@ }, "unit": "1" }, + { + "description": "Errors that occur during the client connection process.", + "name": "mysql.connection.errors", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "182", + "attributes": [ + { + "key": "error", + "value": { + "stringValue": "accept" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "183", + "attributes": [ + { + "key": "error", + "value": { + "stringValue": "internal" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "184", + "attributes": [ + { + "key": "error", + "value": { + "stringValue": "max_connections" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "185", + "attributes": [ + { + "key": "error", + "value": { + "stringValue": "peer_address" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "186", + "attributes": [ + { + "key": "error", + "value": { + "stringValue": "select" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "187", + "attributes": [ + { + "key": "error", + "value": { + "stringValue": "tcpwrap" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + } + ], + "isMonotonic": true + }, + "unit": "1" + }, { "description": "The number of writes to the InnoDB doublewrite buffer.", "name": "mysql.double_writes",