diff --git a/.chloggen/feat_42365.yaml b/.chloggen/feat_42365.yaml new file mode 100644 index 0000000000000..b0525ce84adad --- /dev/null +++ b/.chloggen/feat_42365.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# 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: "receiver/redis" + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: "Add support for redis.mode and redis.sentinel.* metrics" + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [42365] + +# (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: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] diff --git a/receiver/redisreceiver/documentation.md b/receiver/redisreceiver/documentation.md index 251d53bfe5fb8..d78fbbf70d308 100644 --- a/receiver/redisreceiver/documentation.md +++ b/receiver/redisreceiver/documentation.md @@ -431,6 +431,20 @@ The value of the maxmemory configuration directive | ---- | ----------- | ---------- | --------- | | By | Gauge | Int | development | +### redis.mode + +Redis server mode + +| Unit | Metric Type | Value Type | Stability | +| ---- | ----------- | ---------- | --------- | +| {mode} | Gauge | Int | development | + +#### Attributes + +| Name | Description | Values | Requirement Level | +| ---- | ----------- | ------ | -------- | +| mode | Redis server mode | Str: ``cluster``, ``sentinel``, ``standalone`` | Recommended | + ### redis.replication.replica_offset Offset for redis replica @@ -453,6 +467,54 @@ Redis node's role | ---- | ----------- | ------ | -------- | | role | Redis node's role | Str: ``replica``, ``primary`` | Recommended | +### redis.sentinel.masters + +Number of masters monitored by Sentinel. + +| Unit | Metric Type | Value Type | Stability | +| ---- | ----------- | ---------- | --------- | +| {master} | Gauge | Int | development | + +### redis.sentinel.running_scripts + +Number of running Sentinel scripts. + +| Unit | Metric Type | Value Type | Stability | +| ---- | ----------- | ---------- | --------- | +| {script} | Gauge | Int | development | + +### redis.sentinel.scripts_queue_length + +Length of Sentinel scripts queue. + +| Unit | Metric Type | Value Type | Stability | +| ---- | ----------- | ---------- | --------- | +| {script} | Gauge | Int | development | + +### redis.sentinel.simulate_failure_flags + +Simulated failure flags bitmask. + +| Unit | Metric Type | Value Type | Stability | +| ---- | ----------- | ---------- | --------- | +| {flag} | Gauge | Int | development | + +### redis.sentinel.tilt_since_seconds + +Duration in seconds of current TILT, or -1 if not in TILT mode. + +| Unit | Metric Type | Value Type | Stability | +| ---- | ----------- | ---------- | --------- | +| s | Gauge | Int | development | + +### redis.sentinel.total_tilt + +Total TILT occurrences since start. + +| Unit | Metric Type | Value Type | Aggregation Temporality | Monotonic | Stability | +| ---- | ----------- | ---------- | ----------------------- | --------- | --------- | +| {event} | Sum | Int | Cumulative | true | development | + ## Resource Attributes | Name | Description | Values | Enabled | diff --git a/receiver/redisreceiver/integration_test.go b/receiver/redisreceiver/integration_test.go index 65fea884d6f2d..8a031bfa2f69b 100644 --- a/receiver/redisreceiver/integration_test.go +++ b/receiver/redisreceiver/integration_test.go @@ -19,7 +19,10 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/pmetrictest" ) -const redisPort = "6379" +const ( + redisPort = "6379" + sentinelPort = "26379" +) func TestIntegrationV6(t *testing.T) { scraperinttest.NewIntegrationTest( @@ -38,6 +41,8 @@ func TestIntegrationV6(t *testing.T) { scraperinttest.WithCompareOptions( pmetrictest.IgnoreMetricValues(), pmetrictest.IgnoreMetricDataPointsOrder(), + pmetrictest.IgnoreMetricsOrder(), + pmetrictest.IgnoreResourceMetricsOrder(), pmetrictest.IgnoreStartTimestamp(), pmetrictest.IgnoreTimestamp(), pmetrictest.ChangeResourceAttributeValue("server.address", func(_ string) string { @@ -83,6 +88,8 @@ func TestIntegrationV7Cluster(t *testing.T) { scraperinttest.WithCompareOptions( pmetrictest.IgnoreMetricValues(), pmetrictest.IgnoreMetricDataPointsOrder(), + pmetrictest.IgnoreMetricsOrder(), + pmetrictest.IgnoreResourceMetricsOrder(), pmetrictest.IgnoreStartTimestamp(), pmetrictest.IgnoreTimestamp(), ), @@ -91,3 +98,48 @@ func TestIntegrationV7Cluster(t *testing.T) { scraperinttest.WithCompareTimeout(time.Minute), ).Run(t) } + +func TestIntegrationV8Sentinel(t *testing.T) { + scraperinttest.NewIntegrationTest( + NewFactory(), + scraperinttest.WithContainerRequest( + testcontainers.ContainerRequest{ + Image: "redis:8.2.1", + ExposedPorts: []string{sentinelPort}, + Cmd: []string{ + "redis-sentinel", + "/etc/redis/sentinel.conf", + }, + Files: []testcontainers.ContainerFile{ + { + HostFilePath: filepath.Join("testdata", "integration", "redis-sentinel.conf"), + ContainerFilePath: "/etc/redis/sentinel.conf", + FileMode: 0o644, + }, + }, + WaitingFor: wait.ForListeningPort(sentinelPort), + }), + scraperinttest.WithCustomConfig( + func(t *testing.T, cfg component.Config, ci *scraperinttest.ContainerInfo) { + rCfg := cfg.(*Config) + rCfg.Endpoint = fmt.Sprintf("%s:%s", ci.Host(t), ci.MappedPort(t, sentinelPort)) + rCfg.MetricsBuilderConfig.Metrics.RedisMode.Enabled = true + rCfg.MetricsBuilderConfig.Metrics.RedisSentinelMasters.Enabled = true + rCfg.MetricsBuilderConfig.Metrics.RedisSentinelRunningScripts.Enabled = true + rCfg.MetricsBuilderConfig.Metrics.RedisSentinelScriptsQueueLength.Enabled = true + rCfg.MetricsBuilderConfig.Metrics.RedisSentinelSimulateFailureFlags.Enabled = true + rCfg.MetricsBuilderConfig.Metrics.RedisSentinelTiltSinceSeconds.Enabled = true + rCfg.MetricsBuilderConfig.Metrics.RedisSentinelTotalTilt.Enabled = true + }), + scraperinttest.WithCompareOptions( + pmetrictest.IgnoreMetricValues(), + pmetrictest.IgnoreMetricDataPointsOrder(), + pmetrictest.IgnoreMetricsOrder(), + pmetrictest.IgnoreResourceMetricsOrder(), + pmetrictest.IgnoreStartTimestamp(), + pmetrictest.IgnoreTimestamp(), + ), + scraperinttest.WithExpectedFile(filepath.Join("testdata", "integration", "expected-sentinel.yaml")), + scraperinttest.WithCreateContainerTimeout(time.Minute), + ).Run(t) +} diff --git a/receiver/redisreceiver/internal/metadata/generated_config.go b/receiver/redisreceiver/internal/metadata/generated_config.go index b4b4963e685c4..67a2dc64996bb 100644 --- a/receiver/redisreceiver/internal/metadata/generated_config.go +++ b/receiver/redisreceiver/internal/metadata/generated_config.go @@ -66,6 +66,7 @@ type MetricsConfig struct { RedisMemoryPeak MetricConfig `mapstructure:"redis.memory.peak"` RedisMemoryRss MetricConfig `mapstructure:"redis.memory.rss"` RedisMemoryUsed MetricConfig `mapstructure:"redis.memory.used"` + RedisMode MetricConfig `mapstructure:"redis.mode"` RedisNetInput MetricConfig `mapstructure:"redis.net.input"` RedisNetOutput MetricConfig `mapstructure:"redis.net.output"` RedisRdbChangesSinceLastSave MetricConfig `mapstructure:"redis.rdb.changes_since_last_save"` @@ -73,6 +74,12 @@ type MetricsConfig struct { RedisReplicationOffset MetricConfig `mapstructure:"redis.replication.offset"` RedisReplicationReplicaOffset MetricConfig `mapstructure:"redis.replication.replica_offset"` RedisRole MetricConfig `mapstructure:"redis.role"` + RedisSentinelMasters MetricConfig `mapstructure:"redis.sentinel.masters"` + RedisSentinelRunningScripts MetricConfig `mapstructure:"redis.sentinel.running_scripts"` + RedisSentinelScriptsQueueLength MetricConfig `mapstructure:"redis.sentinel.scripts_queue_length"` + RedisSentinelSimulateFailureFlags MetricConfig `mapstructure:"redis.sentinel.simulate_failure_flags"` + RedisSentinelTiltSinceSeconds MetricConfig `mapstructure:"redis.sentinel.tilt_since_seconds"` + RedisSentinelTotalTilt MetricConfig `mapstructure:"redis.sentinel.total_tilt"` RedisSlavesConnected MetricConfig `mapstructure:"redis.slaves.connected"` RedisUptime MetricConfig `mapstructure:"redis.uptime"` } @@ -193,6 +200,9 @@ func DefaultMetricsConfig() MetricsConfig { RedisMemoryUsed: MetricConfig{ Enabled: true, }, + RedisMode: MetricConfig{ + Enabled: false, + }, RedisNetInput: MetricConfig{ Enabled: true, }, @@ -214,6 +224,24 @@ func DefaultMetricsConfig() MetricsConfig { RedisRole: MetricConfig{ Enabled: false, }, + RedisSentinelMasters: MetricConfig{ + Enabled: false, + }, + RedisSentinelRunningScripts: MetricConfig{ + Enabled: false, + }, + RedisSentinelScriptsQueueLength: MetricConfig{ + Enabled: false, + }, + RedisSentinelSimulateFailureFlags: MetricConfig{ + Enabled: false, + }, + RedisSentinelTiltSinceSeconds: MetricConfig{ + Enabled: false, + }, + RedisSentinelTotalTilt: MetricConfig{ + Enabled: false, + }, RedisSlavesConnected: MetricConfig{ Enabled: true, }, diff --git a/receiver/redisreceiver/internal/metadata/generated_config_test.go b/receiver/redisreceiver/internal/metadata/generated_config_test.go index c25494a0eefce..e53089640991b 100644 --- a/receiver/redisreceiver/internal/metadata/generated_config_test.go +++ b/receiver/redisreceiver/internal/metadata/generated_config_test.go @@ -65,6 +65,7 @@ func TestMetricsBuilderConfig(t *testing.T) { RedisMemoryPeak: MetricConfig{Enabled: true}, RedisMemoryRss: MetricConfig{Enabled: true}, RedisMemoryUsed: MetricConfig{Enabled: true}, + RedisMode: MetricConfig{Enabled: true}, RedisNetInput: MetricConfig{Enabled: true}, RedisNetOutput: MetricConfig{Enabled: true}, RedisRdbChangesSinceLastSave: MetricConfig{Enabled: true}, @@ -72,6 +73,12 @@ func TestMetricsBuilderConfig(t *testing.T) { RedisReplicationOffset: MetricConfig{Enabled: true}, RedisReplicationReplicaOffset: MetricConfig{Enabled: true}, RedisRole: MetricConfig{Enabled: true}, + RedisSentinelMasters: MetricConfig{Enabled: true}, + RedisSentinelRunningScripts: MetricConfig{Enabled: true}, + RedisSentinelScriptsQueueLength: MetricConfig{Enabled: true}, + RedisSentinelSimulateFailureFlags: MetricConfig{Enabled: true}, + RedisSentinelTiltSinceSeconds: MetricConfig{Enabled: true}, + RedisSentinelTotalTilt: MetricConfig{Enabled: true}, RedisSlavesConnected: MetricConfig{Enabled: true}, RedisUptime: MetricConfig{Enabled: true}, }, @@ -124,6 +131,7 @@ func TestMetricsBuilderConfig(t *testing.T) { RedisMemoryPeak: MetricConfig{Enabled: false}, RedisMemoryRss: MetricConfig{Enabled: false}, RedisMemoryUsed: MetricConfig{Enabled: false}, + RedisMode: MetricConfig{Enabled: false}, RedisNetInput: MetricConfig{Enabled: false}, RedisNetOutput: MetricConfig{Enabled: false}, RedisRdbChangesSinceLastSave: MetricConfig{Enabled: false}, @@ -131,6 +139,12 @@ func TestMetricsBuilderConfig(t *testing.T) { RedisReplicationOffset: MetricConfig{Enabled: false}, RedisReplicationReplicaOffset: MetricConfig{Enabled: false}, RedisRole: MetricConfig{Enabled: false}, + RedisSentinelMasters: MetricConfig{Enabled: false}, + RedisSentinelRunningScripts: MetricConfig{Enabled: false}, + RedisSentinelScriptsQueueLength: MetricConfig{Enabled: false}, + RedisSentinelSimulateFailureFlags: MetricConfig{Enabled: false}, + RedisSentinelTiltSinceSeconds: MetricConfig{Enabled: false}, + RedisSentinelTotalTilt: MetricConfig{Enabled: false}, RedisSlavesConnected: MetricConfig{Enabled: false}, RedisUptime: MetricConfig{Enabled: false}, }, diff --git a/receiver/redisreceiver/internal/metadata/generated_metrics.go b/receiver/redisreceiver/internal/metadata/generated_metrics.go index b3aa4924a01dc..5843e4ae0d01c 100644 --- a/receiver/redisreceiver/internal/metadata/generated_metrics.go +++ b/receiver/redisreceiver/internal/metadata/generated_metrics.go @@ -38,6 +38,36 @@ var MapAttributeClusterState = map[string]AttributeClusterState{ "fail": AttributeClusterStateFail, } +// AttributeMode specifies the value mode attribute. +type AttributeMode int + +const ( + _ AttributeMode = iota + AttributeModeCluster + AttributeModeSentinel + AttributeModeStandalone +) + +// String returns the string representation of the AttributeMode. +func (av AttributeMode) String() string { + switch av { + case AttributeModeCluster: + return "cluster" + case AttributeModeSentinel: + return "sentinel" + case AttributeModeStandalone: + return "standalone" + } + return "" +} + +// MapAttributeMode is a helper map of string to AttributeMode attribute value. +var MapAttributeMode = map[string]AttributeMode{ + "cluster": AttributeModeCluster, + "sentinel": AttributeModeSentinel, + "standalone": AttributeModeStandalone, +} + // AttributePercentile specifies the value percentile attribute. type AttributePercentile int @@ -251,6 +281,9 @@ var MetricsInfo = metricsInfo{ RedisMemoryUsed: metricInfo{ Name: "redis.memory.used", }, + RedisMode: metricInfo{ + Name: "redis.mode", + }, RedisNetInput: metricInfo{ Name: "redis.net.input", }, @@ -272,6 +305,24 @@ var MetricsInfo = metricsInfo{ RedisRole: metricInfo{ Name: "redis.role", }, + RedisSentinelMasters: metricInfo{ + Name: "redis.sentinel.masters", + }, + RedisSentinelRunningScripts: metricInfo{ + Name: "redis.sentinel.running_scripts", + }, + RedisSentinelScriptsQueueLength: metricInfo{ + Name: "redis.sentinel.scripts_queue_length", + }, + RedisSentinelSimulateFailureFlags: metricInfo{ + Name: "redis.sentinel.simulate_failure_flags", + }, + RedisSentinelTiltSinceSeconds: metricInfo{ + Name: "redis.sentinel.tilt_since_seconds", + }, + RedisSentinelTotalTilt: metricInfo{ + Name: "redis.sentinel.total_tilt", + }, RedisSlavesConnected: metricInfo{ Name: "redis.slaves.connected", }, @@ -319,6 +370,7 @@ type metricsInfo struct { RedisMemoryPeak metricInfo RedisMemoryRss metricInfo RedisMemoryUsed metricInfo + RedisMode metricInfo RedisNetInput metricInfo RedisNetOutput metricInfo RedisRdbChangesSinceLastSave metricInfo @@ -326,6 +378,12 @@ type metricsInfo struct { RedisReplicationOffset metricInfo RedisReplicationReplicaOffset metricInfo RedisRole metricInfo + RedisSentinelMasters metricInfo + RedisSentinelRunningScripts metricInfo + RedisSentinelScriptsQueueLength metricInfo + RedisSentinelSimulateFailureFlags metricInfo + RedisSentinelTiltSinceSeconds metricInfo + RedisSentinelTotalTilt metricInfo RedisSlavesConnected metricInfo RedisUptime metricInfo } @@ -2243,6 +2301,57 @@ func newMetricRedisMemoryUsed(cfg MetricConfig) metricRedisMemoryUsed { return m } +type metricRedisMode struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills redis.mode metric with initial data. +func (m *metricRedisMode) init() { + m.data.SetName("redis.mode") + m.data.SetDescription("Redis server mode") + m.data.SetUnit("{mode}") + m.data.SetEmptyGauge() + m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) +} + +func (m *metricRedisMode) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, modeAttributeValue string) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) + dp.Attributes().PutStr("mode", modeAttributeValue) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricRedisMode) updateCapacity() { + if m.data.Gauge().DataPoints().Len() > m.capacity { + m.capacity = m.data.Gauge().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricRedisMode) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricRedisMode(cfg MetricConfig) metricRedisMode { + m := metricRedisMode{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + type metricRedisNetInput struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. @@ -2596,6 +2705,302 @@ func newMetricRedisRole(cfg MetricConfig) metricRedisRole { return m } +type metricRedisSentinelMasters struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills redis.sentinel.masters metric with initial data. +func (m *metricRedisSentinelMasters) init() { + m.data.SetName("redis.sentinel.masters") + m.data.SetDescription("Number of masters monitored by Sentinel.") + m.data.SetUnit("{master}") + m.data.SetEmptyGauge() +} + +func (m *metricRedisSentinelMasters) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricRedisSentinelMasters) updateCapacity() { + if m.data.Gauge().DataPoints().Len() > m.capacity { + m.capacity = m.data.Gauge().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricRedisSentinelMasters) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricRedisSentinelMasters(cfg MetricConfig) metricRedisSentinelMasters { + m := metricRedisSentinelMasters{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricRedisSentinelRunningScripts struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills redis.sentinel.running_scripts metric with initial data. +func (m *metricRedisSentinelRunningScripts) init() { + m.data.SetName("redis.sentinel.running_scripts") + m.data.SetDescription("Number of running Sentinel scripts.") + m.data.SetUnit("{script}") + m.data.SetEmptyGauge() +} + +func (m *metricRedisSentinelRunningScripts) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricRedisSentinelRunningScripts) updateCapacity() { + if m.data.Gauge().DataPoints().Len() > m.capacity { + m.capacity = m.data.Gauge().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricRedisSentinelRunningScripts) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricRedisSentinelRunningScripts(cfg MetricConfig) metricRedisSentinelRunningScripts { + m := metricRedisSentinelRunningScripts{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricRedisSentinelScriptsQueueLength struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills redis.sentinel.scripts_queue_length metric with initial data. +func (m *metricRedisSentinelScriptsQueueLength) init() { + m.data.SetName("redis.sentinel.scripts_queue_length") + m.data.SetDescription("Length of Sentinel scripts queue.") + m.data.SetUnit("{script}") + m.data.SetEmptyGauge() +} + +func (m *metricRedisSentinelScriptsQueueLength) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricRedisSentinelScriptsQueueLength) updateCapacity() { + if m.data.Gauge().DataPoints().Len() > m.capacity { + m.capacity = m.data.Gauge().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricRedisSentinelScriptsQueueLength) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricRedisSentinelScriptsQueueLength(cfg MetricConfig) metricRedisSentinelScriptsQueueLength { + m := metricRedisSentinelScriptsQueueLength{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricRedisSentinelSimulateFailureFlags struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills redis.sentinel.simulate_failure_flags metric with initial data. +func (m *metricRedisSentinelSimulateFailureFlags) init() { + m.data.SetName("redis.sentinel.simulate_failure_flags") + m.data.SetDescription("Simulated failure flags bitmask.") + m.data.SetUnit("{flag}") + m.data.SetEmptyGauge() +} + +func (m *metricRedisSentinelSimulateFailureFlags) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricRedisSentinelSimulateFailureFlags) updateCapacity() { + if m.data.Gauge().DataPoints().Len() > m.capacity { + m.capacity = m.data.Gauge().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricRedisSentinelSimulateFailureFlags) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricRedisSentinelSimulateFailureFlags(cfg MetricConfig) metricRedisSentinelSimulateFailureFlags { + m := metricRedisSentinelSimulateFailureFlags{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricRedisSentinelTiltSinceSeconds struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills redis.sentinel.tilt_since_seconds metric with initial data. +func (m *metricRedisSentinelTiltSinceSeconds) init() { + m.data.SetName("redis.sentinel.tilt_since_seconds") + m.data.SetDescription("Duration in seconds of current TILT, or -1 if not in TILT mode.") + m.data.SetUnit("s") + m.data.SetEmptyGauge() +} + +func (m *metricRedisSentinelTiltSinceSeconds) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricRedisSentinelTiltSinceSeconds) updateCapacity() { + if m.data.Gauge().DataPoints().Len() > m.capacity { + m.capacity = m.data.Gauge().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricRedisSentinelTiltSinceSeconds) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricRedisSentinelTiltSinceSeconds(cfg MetricConfig) metricRedisSentinelTiltSinceSeconds { + m := metricRedisSentinelTiltSinceSeconds{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricRedisSentinelTotalTilt struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills redis.sentinel.total_tilt metric with initial data. +func (m *metricRedisSentinelTotalTilt) init() { + m.data.SetName("redis.sentinel.total_tilt") + m.data.SetDescription("Total TILT occurrences since start.") + m.data.SetUnit("{event}") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(true) + m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) +} + +func (m *metricRedisSentinelTotalTilt) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.config.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricRedisSentinelTotalTilt) 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 *metricRedisSentinelTotalTilt) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricRedisSentinelTotalTilt(cfg MetricConfig) metricRedisSentinelTotalTilt { + m := metricRedisSentinelTotalTilt{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + type metricRedisSlavesConnected struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. @@ -2746,6 +3151,7 @@ type MetricsBuilder struct { metricRedisMemoryPeak metricRedisMemoryPeak metricRedisMemoryRss metricRedisMemoryRss metricRedisMemoryUsed metricRedisMemoryUsed + metricRedisMode metricRedisMode metricRedisNetInput metricRedisNetInput metricRedisNetOutput metricRedisNetOutput metricRedisRdbChangesSinceLastSave metricRedisRdbChangesSinceLastSave @@ -2753,6 +3159,12 @@ type MetricsBuilder struct { metricRedisReplicationOffset metricRedisReplicationOffset metricRedisReplicationReplicaOffset metricRedisReplicationReplicaOffset metricRedisRole metricRedisRole + metricRedisSentinelMasters metricRedisSentinelMasters + metricRedisSentinelRunningScripts metricRedisSentinelRunningScripts + metricRedisSentinelScriptsQueueLength metricRedisSentinelScriptsQueueLength + metricRedisSentinelSimulateFailureFlags metricRedisSentinelSimulateFailureFlags + metricRedisSentinelTiltSinceSeconds metricRedisSentinelTiltSinceSeconds + metricRedisSentinelTotalTilt metricRedisSentinelTotalTilt metricRedisSlavesConnected metricRedisSlavesConnected metricRedisUptime metricRedisUptime } @@ -2818,6 +3230,7 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.Settings, opt metricRedisMemoryPeak: newMetricRedisMemoryPeak(mbc.Metrics.RedisMemoryPeak), metricRedisMemoryRss: newMetricRedisMemoryRss(mbc.Metrics.RedisMemoryRss), metricRedisMemoryUsed: newMetricRedisMemoryUsed(mbc.Metrics.RedisMemoryUsed), + metricRedisMode: newMetricRedisMode(mbc.Metrics.RedisMode), metricRedisNetInput: newMetricRedisNetInput(mbc.Metrics.RedisNetInput), metricRedisNetOutput: newMetricRedisNetOutput(mbc.Metrics.RedisNetOutput), metricRedisRdbChangesSinceLastSave: newMetricRedisRdbChangesSinceLastSave(mbc.Metrics.RedisRdbChangesSinceLastSave), @@ -2825,6 +3238,12 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.Settings, opt metricRedisReplicationOffset: newMetricRedisReplicationOffset(mbc.Metrics.RedisReplicationOffset), metricRedisReplicationReplicaOffset: newMetricRedisReplicationReplicaOffset(mbc.Metrics.RedisReplicationReplicaOffset), metricRedisRole: newMetricRedisRole(mbc.Metrics.RedisRole), + metricRedisSentinelMasters: newMetricRedisSentinelMasters(mbc.Metrics.RedisSentinelMasters), + metricRedisSentinelRunningScripts: newMetricRedisSentinelRunningScripts(mbc.Metrics.RedisSentinelRunningScripts), + metricRedisSentinelScriptsQueueLength: newMetricRedisSentinelScriptsQueueLength(mbc.Metrics.RedisSentinelScriptsQueueLength), + metricRedisSentinelSimulateFailureFlags: newMetricRedisSentinelSimulateFailureFlags(mbc.Metrics.RedisSentinelSimulateFailureFlags), + metricRedisSentinelTiltSinceSeconds: newMetricRedisSentinelTiltSinceSeconds(mbc.Metrics.RedisSentinelTiltSinceSeconds), + metricRedisSentinelTotalTilt: newMetricRedisSentinelTotalTilt(mbc.Metrics.RedisSentinelTotalTilt), metricRedisSlavesConnected: newMetricRedisSlavesConnected(mbc.Metrics.RedisSlavesConnected), metricRedisUptime: newMetricRedisUptime(mbc.Metrics.RedisUptime), resourceAttributeIncludeFilter: make(map[string]filter.Filter), @@ -2955,6 +3374,7 @@ func (mb *MetricsBuilder) EmitForResource(options ...ResourceMetricsOption) { mb.metricRedisMemoryPeak.emit(ils.Metrics()) mb.metricRedisMemoryRss.emit(ils.Metrics()) mb.metricRedisMemoryUsed.emit(ils.Metrics()) + mb.metricRedisMode.emit(ils.Metrics()) mb.metricRedisNetInput.emit(ils.Metrics()) mb.metricRedisNetOutput.emit(ils.Metrics()) mb.metricRedisRdbChangesSinceLastSave.emit(ils.Metrics()) @@ -2962,6 +3382,12 @@ func (mb *MetricsBuilder) EmitForResource(options ...ResourceMetricsOption) { mb.metricRedisReplicationOffset.emit(ils.Metrics()) mb.metricRedisReplicationReplicaOffset.emit(ils.Metrics()) mb.metricRedisRole.emit(ils.Metrics()) + mb.metricRedisSentinelMasters.emit(ils.Metrics()) + mb.metricRedisSentinelRunningScripts.emit(ils.Metrics()) + mb.metricRedisSentinelScriptsQueueLength.emit(ils.Metrics()) + mb.metricRedisSentinelSimulateFailureFlags.emit(ils.Metrics()) + mb.metricRedisSentinelTiltSinceSeconds.emit(ils.Metrics()) + mb.metricRedisSentinelTotalTilt.emit(ils.Metrics()) mb.metricRedisSlavesConnected.emit(ils.Metrics()) mb.metricRedisUptime.emit(ils.Metrics()) @@ -3185,6 +3611,11 @@ func (mb *MetricsBuilder) RecordRedisMemoryUsedDataPoint(ts pcommon.Timestamp, v mb.metricRedisMemoryUsed.recordDataPoint(mb.startTime, ts, val) } +// RecordRedisModeDataPoint adds a data point to redis.mode metric. +func (mb *MetricsBuilder) RecordRedisModeDataPoint(ts pcommon.Timestamp, val int64, modeAttributeValue AttributeMode) { + mb.metricRedisMode.recordDataPoint(mb.startTime, ts, val, modeAttributeValue.String()) +} + // RecordRedisNetInputDataPoint adds a data point to redis.net.input metric. func (mb *MetricsBuilder) RecordRedisNetInputDataPoint(ts pcommon.Timestamp, val int64) { mb.metricRedisNetInput.recordDataPoint(mb.startTime, ts, val) @@ -3220,6 +3651,36 @@ func (mb *MetricsBuilder) RecordRedisRoleDataPoint(ts pcommon.Timestamp, val int mb.metricRedisRole.recordDataPoint(mb.startTime, ts, val, roleAttributeValue.String()) } +// RecordRedisSentinelMastersDataPoint adds a data point to redis.sentinel.masters metric. +func (mb *MetricsBuilder) RecordRedisSentinelMastersDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricRedisSentinelMasters.recordDataPoint(mb.startTime, ts, val) +} + +// RecordRedisSentinelRunningScriptsDataPoint adds a data point to redis.sentinel.running_scripts metric. +func (mb *MetricsBuilder) RecordRedisSentinelRunningScriptsDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricRedisSentinelRunningScripts.recordDataPoint(mb.startTime, ts, val) +} + +// RecordRedisSentinelScriptsQueueLengthDataPoint adds a data point to redis.sentinel.scripts_queue_length metric. +func (mb *MetricsBuilder) RecordRedisSentinelScriptsQueueLengthDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricRedisSentinelScriptsQueueLength.recordDataPoint(mb.startTime, ts, val) +} + +// RecordRedisSentinelSimulateFailureFlagsDataPoint adds a data point to redis.sentinel.simulate_failure_flags metric. +func (mb *MetricsBuilder) RecordRedisSentinelSimulateFailureFlagsDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricRedisSentinelSimulateFailureFlags.recordDataPoint(mb.startTime, ts, val) +} + +// RecordRedisSentinelTiltSinceSecondsDataPoint adds a data point to redis.sentinel.tilt_since_seconds metric. +func (mb *MetricsBuilder) RecordRedisSentinelTiltSinceSecondsDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricRedisSentinelTiltSinceSeconds.recordDataPoint(mb.startTime, ts, val) +} + +// RecordRedisSentinelTotalTiltDataPoint adds a data point to redis.sentinel.total_tilt metric. +func (mb *MetricsBuilder) RecordRedisSentinelTotalTiltDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricRedisSentinelTotalTilt.recordDataPoint(mb.startTime, ts, val) +} + // RecordRedisSlavesConnectedDataPoint adds a data point to redis.slaves.connected metric. func (mb *MetricsBuilder) RecordRedisSlavesConnectedDataPoint(ts pcommon.Timestamp, val int64) { mb.metricRedisSlavesConnected.recordDataPoint(mb.startTime, ts, val) diff --git a/receiver/redisreceiver/internal/metadata/generated_metrics_test.go b/receiver/redisreceiver/internal/metadata/generated_metrics_test.go index 261448f01384c..711ca7f6c4703 100644 --- a/receiver/redisreceiver/internal/metadata/generated_metrics_test.go +++ b/receiver/redisreceiver/internal/metadata/generated_metrics_test.go @@ -204,6 +204,9 @@ func TestMetricsBuilder(t *testing.T) { allMetricsCount++ mb.RecordRedisMemoryUsedDataPoint(ts, 1) + allMetricsCount++ + mb.RecordRedisModeDataPoint(ts, 1, AttributeModeCluster) + defaultMetricsCount++ allMetricsCount++ mb.RecordRedisNetInputDataPoint(ts, 1) @@ -230,6 +233,24 @@ func TestMetricsBuilder(t *testing.T) { allMetricsCount++ mb.RecordRedisRoleDataPoint(ts, 1, AttributeRoleReplica) + allMetricsCount++ + mb.RecordRedisSentinelMastersDataPoint(ts, 1) + + allMetricsCount++ + mb.RecordRedisSentinelRunningScriptsDataPoint(ts, 1) + + allMetricsCount++ + mb.RecordRedisSentinelScriptsQueueLengthDataPoint(ts, 1) + + allMetricsCount++ + mb.RecordRedisSentinelSimulateFailureFlagsDataPoint(ts, 1) + + allMetricsCount++ + mb.RecordRedisSentinelTiltSinceSecondsDataPoint(ts, 1) + + allMetricsCount++ + mb.RecordRedisSentinelTotalTiltDataPoint(ts, 1) + defaultMetricsCount++ allMetricsCount++ mb.RecordRedisSlavesConnectedDataPoint(ts, 1) @@ -777,6 +798,21 @@ func TestMetricsBuilder(t *testing.T) { assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) assert.Equal(t, int64(1), dp.IntValue()) + case "redis.mode": + assert.False(t, validatedMetrics["redis.mode"], "Found a duplicate in the metrics slice: redis.mode") + validatedMetrics["redis.mode"] = true + assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.Equal(t, "Redis server mode", ms.At(i).Description()) + assert.Equal(t, "{mode}", ms.At(i).Unit()) + dp := ms.At(i).Gauge().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("mode") + assert.True(t, ok) + assert.Equal(t, "cluster", attrVal.Str()) case "redis.net.input": assert.False(t, validatedMetrics["redis.net.input"], "Found a duplicate in the metrics slice: redis.net.input") validatedMetrics["redis.net.input"] = true @@ -872,6 +908,80 @@ func TestMetricsBuilder(t *testing.T) { attrVal, ok := dp.Attributes().Get("role") assert.True(t, ok) assert.Equal(t, "replica", attrVal.Str()) + case "redis.sentinel.masters": + assert.False(t, validatedMetrics["redis.sentinel.masters"], "Found a duplicate in the metrics slice: redis.sentinel.masters") + validatedMetrics["redis.sentinel.masters"] = true + assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.Equal(t, "Number of masters monitored by Sentinel.", ms.At(i).Description()) + assert.Equal(t, "{master}", ms.At(i).Unit()) + dp := ms.At(i).Gauge().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()) + case "redis.sentinel.running_scripts": + assert.False(t, validatedMetrics["redis.sentinel.running_scripts"], "Found a duplicate in the metrics slice: redis.sentinel.running_scripts") + validatedMetrics["redis.sentinel.running_scripts"] = true + assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.Equal(t, "Number of running Sentinel scripts.", ms.At(i).Description()) + assert.Equal(t, "{script}", ms.At(i).Unit()) + dp := ms.At(i).Gauge().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()) + case "redis.sentinel.scripts_queue_length": + assert.False(t, validatedMetrics["redis.sentinel.scripts_queue_length"], "Found a duplicate in the metrics slice: redis.sentinel.scripts_queue_length") + validatedMetrics["redis.sentinel.scripts_queue_length"] = true + assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.Equal(t, "Length of Sentinel scripts queue.", ms.At(i).Description()) + assert.Equal(t, "{script}", ms.At(i).Unit()) + dp := ms.At(i).Gauge().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()) + case "redis.sentinel.simulate_failure_flags": + assert.False(t, validatedMetrics["redis.sentinel.simulate_failure_flags"], "Found a duplicate in the metrics slice: redis.sentinel.simulate_failure_flags") + validatedMetrics["redis.sentinel.simulate_failure_flags"] = true + assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.Equal(t, "Simulated failure flags bitmask.", ms.At(i).Description()) + assert.Equal(t, "{flag}", ms.At(i).Unit()) + dp := ms.At(i).Gauge().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()) + case "redis.sentinel.tilt_since_seconds": + assert.False(t, validatedMetrics["redis.sentinel.tilt_since_seconds"], "Found a duplicate in the metrics slice: redis.sentinel.tilt_since_seconds") + validatedMetrics["redis.sentinel.tilt_since_seconds"] = true + assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.Equal(t, "Duration in seconds of current TILT, or -1 if not in TILT mode.", ms.At(i).Description()) + assert.Equal(t, "s", ms.At(i).Unit()) + dp := ms.At(i).Gauge().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()) + case "redis.sentinel.total_tilt": + assert.False(t, validatedMetrics["redis.sentinel.total_tilt"], "Found a duplicate in the metrics slice: redis.sentinel.total_tilt") + validatedMetrics["redis.sentinel.total_tilt"] = true + assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, "Total TILT occurrences since start.", ms.At(i).Description()) + assert.Equal(t, "{event}", ms.At(i).Unit()) + assert.True(t, 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()) case "redis.slaves.connected": assert.False(t, validatedMetrics["redis.slaves.connected"], "Found a duplicate in the metrics slice: redis.slaves.connected") validatedMetrics["redis.slaves.connected"] = true diff --git a/receiver/redisreceiver/internal/metadata/testdata/config.yaml b/receiver/redisreceiver/internal/metadata/testdata/config.yaml index 698cf2b13b634..dcd9639fbcf23 100644 --- a/receiver/redisreceiver/internal/metadata/testdata/config.yaml +++ b/receiver/redisreceiver/internal/metadata/testdata/config.yaml @@ -77,6 +77,8 @@ all_set: enabled: true redis.memory.used: enabled: true + redis.mode: + enabled: true redis.net.input: enabled: true redis.net.output: @@ -91,6 +93,18 @@ all_set: enabled: true redis.role: enabled: true + redis.sentinel.masters: + enabled: true + redis.sentinel.running_scripts: + enabled: true + redis.sentinel.scripts_queue_length: + enabled: true + redis.sentinel.simulate_failure_flags: + enabled: true + redis.sentinel.tilt_since_seconds: + enabled: true + redis.sentinel.total_tilt: + enabled: true redis.slaves.connected: enabled: true redis.uptime: @@ -180,6 +194,8 @@ none_set: enabled: false redis.memory.used: enabled: false + redis.mode: + enabled: false redis.net.input: enabled: false redis.net.output: @@ -194,6 +210,18 @@ none_set: enabled: false redis.role: enabled: false + redis.sentinel.masters: + enabled: false + redis.sentinel.running_scripts: + enabled: false + redis.sentinel.scripts_queue_length: + enabled: false + redis.sentinel.simulate_failure_flags: + enabled: false + redis.sentinel.tilt_since_seconds: + enabled: false + redis.sentinel.total_tilt: + enabled: false redis.slaves.connected: enabled: false redis.uptime: diff --git a/receiver/redisreceiver/metadata.yaml b/receiver/redisreceiver/metadata.yaml index 2ba76993a3513..88025f7ea71fc 100644 --- a/receiver/redisreceiver/metadata.yaml +++ b/receiver/redisreceiver/metadata.yaml @@ -35,6 +35,13 @@ attributes: db: description: Redis database identifier type: string + mode: + description: Redis server mode + type: string + enum: + - cluster + - sentinel + - standalone percentile: description: Percentile type: string @@ -79,7 +86,6 @@ metrics: monotonic: false aggregation_temporality: cumulative - redis.clients.connected: enabled: true description: Number of client connections (excluding connections from replicas) @@ -147,7 +153,6 @@ metrics: gauge: value_type: int - redis.cluster.slots_assigned: enabled: false description: "Number of slots assigned in the cluster" @@ -193,6 +198,7 @@ metrics: gauge: value_type: int attributes: [cluster_state] + redis.cluster.stats_messages_received: enabled: false description: "Total number of messages received by the cluster" @@ -257,6 +263,7 @@ metrics: monotonic: true aggregation_temporality: cumulative attributes: [cmd] + redis.commands: enabled: true description: Number of commands processed per second @@ -330,6 +337,7 @@ metrics: gauge: value_type: int attributes: [db] + redis.db.keys: enabled: true description: "Number of keyspace keys" @@ -339,6 +347,7 @@ metrics: gauge: value_type: int attributes: [db] + redis.keys.evicted: enabled: true description: Number of evicted keys due to maxmemory limit @@ -384,6 +393,7 @@ metrics: monotonic: true aggregation_temporality: cumulative + redis.latest_fork: enabled: true description: Duration of the latest fork operation in microseconds @@ -447,6 +457,16 @@ metrics: gauge: value_type: int + redis.mode: + enabled: false + description: Redis server mode + stability: + level: development + unit: "{mode}" + gauge: + value_type: int + attributes: [mode] + redis.net.input: enabled: true description: The total number of bytes read from the network @@ -519,6 +539,63 @@ metrics: monotonic: false aggregation_temporality: cumulative attributes: [role] + + redis.sentinel.masters: + enabled: false + description: Number of masters monitored by Sentinel. + stability: + level: development + unit: "{master}" + gauge: + value_type: int + + redis.sentinel.running_scripts: + enabled: false + description: Number of running Sentinel scripts. + stability: + level: development + unit: "{script}" + gauge: + value_type: int + + redis.sentinel.scripts_queue_length: + enabled: false + description: Length of Sentinel scripts queue. + stability: + level: development + unit: "{script}" + gauge: + value_type: int + + redis.sentinel.simulate_failure_flags: + enabled: false + description: Simulated failure flags bitmask. + stability: + level: development + unit: "{flag}" + gauge: + value_type: int + + redis.sentinel.tilt_since_seconds: + enabled: false + description: Duration in seconds of current TILT, or -1 if not in TILT mode. + stability: + level: development + unit: "s" + gauge: + value_type: int + + redis.sentinel.total_tilt: + enabled: false + description: Total TILT occurrences since start. + stability: + level: development + unit: "{event}" + sum: + value_type: int + monotonic: true + aggregation_temporality: cumulative + redis.slaves.connected: enabled: true description: Number of connected replicas @@ -530,7 +607,6 @@ metrics: monotonic: false aggregation_temporality: cumulative - redis.uptime: enabled: true description: Number of seconds since Redis server start diff --git a/receiver/redisreceiver/redis_scraper.go b/receiver/redisreceiver/redis_scraper.go index 96a12106b2827..7616ac1f9d4ee 100644 --- a/receiver/redisreceiver/redis_scraper.go +++ b/receiver/redisreceiver/redis_scraper.go @@ -95,10 +95,14 @@ func (rs *redisScraper) Scrape(context.Context) (pmetric.Metrics, error) { } rs.uptime = currentUptime - rs.recordCommonMetrics(now, inf) + mode := rs.getRedisMode(inf) + + rs.recordCommonMetrics(now, inf, rs.dataPointRecorders()) + rs.recordCommonMetrics(now, inf, rs.sentinelDataPointRecorders()) rs.recordKeyspaceMetrics(now, inf) rs.recordRoleMetrics(now, inf) rs.recordCmdMetrics(now, inf) + rs.recordModeMetrics(now, mode) rb := rs.mb.NewResourceBuilder() rb.SetRedisVersion(rs.getRedisVersion(inf)) rb.SetServerAddress(rs.configInfo.Address) @@ -107,8 +111,7 @@ func (rs *redisScraper) Scrape(context.Context) (pmetric.Metrics, error) { } // recordCommonMetrics records metrics from Redis info key-value pairs. -func (rs *redisScraper) recordCommonMetrics(ts pcommon.Timestamp, inf info) { - recorders := rs.dataPointRecorders() +func (rs *redisScraper) recordCommonMetrics(ts pcommon.Timestamp, inf info, recorders map[string]any) { for infoKey, infoVal := range inf { recorder, ok := recorders[infoKey] if !ok { @@ -121,20 +124,25 @@ func (rs *redisScraper) recordCommonMetrics(ts pcommon.Timestamp, inf info) { if err != nil { rs.settings.Logger.Warn("failed to parse info int val", zap.String("key", infoKey), zap.String("val", infoVal), zap.Error(err)) + continue } recordDataPoint(ts, val) + case func(pcommon.Timestamp, float64): val, err := strconv.ParseFloat(infoVal, 64) if err != nil { rs.settings.Logger.Warn("failed to parse info float val", zap.String("key", infoKey), zap.String("val", infoVal), zap.Error(err)) + continue } recordDataPoint(ts, val) + case func(pcommon.Timestamp, int64, metadata.AttributeClusterState): val, err := strconv.ParseInt(infoVal, 10, 64) if err != nil { rs.settings.Logger.Warn("failed to parse info int val", zap.String("key", infoKey), zap.String("val", infoVal), zap.Error(err)) + continue } var state metadata.AttributeClusterState if infoKey == "cluster_state" { @@ -179,6 +187,28 @@ func (*redisScraper) getRedisVersion(inf info) string { return "unknown" } +// getRedisMode retrieves mode string from 'redis_mode' Redis info key-value pairs +// e.g. "redis_mode:standalone" +func (*redisScraper) getRedisMode(inf info) string { + if str, ok := inf["redis_mode"]; ok { + return str + } + return "unknown" +} + +// recordModeMetrics records metrics from 'redis_mode' Redis info key-value pairs +// e.g. "redis_mode:standalone" +func (rs *redisScraper) recordModeMetrics(ts pcommon.Timestamp, mode string) { + switch mode { + case "cluster": + rs.mb.RecordRedisModeDataPoint(ts, 1, metadata.AttributeModeCluster) + case "sentinel": + rs.mb.RecordRedisModeDataPoint(ts, 1, metadata.AttributeModeSentinel) + case "standalone": + rs.mb.RecordRedisModeDataPoint(ts, 1, metadata.AttributeModeStandalone) + } +} + // recordRoleMetrics records metrics from 'role' Redis info key-value pairs // e.g. "role:master" func (rs *redisScraper) recordRoleMetrics(ts pcommon.Timestamp, inf info) { @@ -248,3 +278,15 @@ func (rs *redisScraper) recordCmdLatencyMetrics(ts pcommon.Timestamp, cmd, val s } } } + +// sentinelDataPointRecorders returns the map of supported Sentinel metrics. +func (rs *redisScraper) sentinelDataPointRecorders() map[string]any { + return map[string]any{ + "sentinel_masters": rs.mb.RecordRedisSentinelMastersDataPoint, + "sentinel_tilt_since_seconds": rs.mb.RecordRedisSentinelTiltSinceSecondsDataPoint, + "sentinel_total_tilt": rs.mb.RecordRedisSentinelTotalTiltDataPoint, + "sentinel_running_scripts": rs.mb.RecordRedisSentinelRunningScriptsDataPoint, + "sentinel_scripts_queue_length": rs.mb.RecordRedisSentinelScriptsQueueLengthDataPoint, + "sentinel_simulate_failure_flags": rs.mb.RecordRedisSentinelSimulateFailureFlagsDataPoint, + } +} diff --git a/receiver/redisreceiver/testdata/integration/expected-sentinel.yaml b/receiver/redisreceiver/testdata/integration/expected-sentinel.yaml new file mode 100644 index 0000000000000..248c12147a944 --- /dev/null +++ b/receiver/redisreceiver/testdata/integration/expected-sentinel.yaml @@ -0,0 +1,272 @@ +resourceMetrics: + - resource: + attributes: + - key: redis.version + value: + stringValue: 8.2.1 + scopeMetrics: + - metrics: + - description: Number of clients pending on a blocking call + name: redis.clients.blocked + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "0" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + unit: "{client}" + - description: Number of client connections (excluding connections from replicas) + name: redis.clients.connected + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "1" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + unit: "{client}" + - description: Biggest input buffer among current client connections + gauge: + dataPoints: + - asInt: "0" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + name: redis.clients.max_input_buffer + unit: By + - description: Longest output list among current client connections + gauge: + dataPoints: + - asInt: "0" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + name: redis.clients.max_output_buffer + unit: By + - description: Number of commands processed per second + gauge: + dataPoints: + - asInt: "0" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + name: redis.commands + unit: "{ops}/s" + - description: Total number of commands processed by the server + name: redis.commands.processed + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "8" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + isMonotonic: true + unit: "{command}" + - description: Total number of connections accepted by the server + name: redis.connections.received + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "1" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + isMonotonic: true + unit: "{connection}" + - description: Number of connections rejected because of maxclients limit + name: redis.connections.rejected + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "0" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + isMonotonic: true + unit: "{connection}" + - description: System CPU consumed by the Redis server in seconds since server start + name: redis.cpu.time + sum: + aggregationTemporality: 2 + dataPoints: + - asDouble: 0.31895 + attributes: + - key: state + value: + stringValue: sys + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + - asDouble: 0.004504 + attributes: + - key: state + value: + stringValue: sys_children + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + - asDouble: 0.31858 + attributes: + - key: state + value: + stringValue: sys_main_thread + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + - asDouble: 1.101488 + attributes: + - key: state + value: + stringValue: user + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + - asDouble: 0.004405 + attributes: + - key: state + value: + stringValue: user_children + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + - asDouble: 1.100888 + attributes: + - key: state + value: + stringValue: user_main_thread + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + isMonotonic: true + unit: s + - description: Number of evicted keys due to maxmemory limit + name: redis.keys.evicted + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "0" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + isMonotonic: true + unit: "{key}" + - description: Total number of key expiration events + name: redis.keys.expired + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "0" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + isMonotonic: true + unit: "{event}" + - description: Number of successful lookup of keys in the main dictionary + name: redis.keyspace.hits + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "0" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + isMonotonic: true + unit: "{hit}" + - description: Number of failed lookup of keys in the main dictionary + name: redis.keyspace.misses + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "0" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + isMonotonic: true + unit: "{miss}" + - description: Duration of the latest fork operation in microseconds + gauge: + dataPoints: + - asInt: "0" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + name: redis.latest_fork + unit: us + - description: Redis server mode + gauge: + dataPoints: + - asInt: "1" + attributes: + - key: mode + value: + stringValue: sentinel + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + name: redis.mode + unit: "{mode}" + - description: The total number of bytes read from the network + name: redis.net.input + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "458" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + isMonotonic: true + unit: By + - description: The total number of bytes written to the network + name: redis.net.output + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "42172" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + isMonotonic: true + unit: By + - description: Number of masters monitored by Sentinel. + name: redis.sentinel.masters + gauge: + dataPoints: + - asInt: "1" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + unit: "{master}" + - description: Number of running Sentinel scripts. + gauge: + dataPoints: + - asInt: "0" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + name: redis.sentinel.running_scripts + unit: "{script}" + - description: Length of Sentinel scripts queue. + gauge: + dataPoints: + - asInt: "0" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + name: redis.sentinel.scripts_queue_length + unit: "{script}" + - description: Simulated failure flags bitmask. + gauge: + dataPoints: + - asInt: "0" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + name: redis.sentinel.simulate_failure_flags + unit: "{flag}" + - description: Duration in seconds of current TILT, or -1 if not in TILT mode. + gauge: + dataPoints: + - asInt: "-1" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + name: redis.sentinel.tilt_since_seconds + unit: s + - description: Total TILT occurrences since start. + name: redis.sentinel.total_tilt + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "0" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + isMonotonic: true + unit: "{event}" + - description: Number of seconds since Redis server start + name: redis.uptime + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "156" + startTimeUnixNano: "1687339331739210000" + timeUnixNano: "1687339332739210000" + isMonotonic: true + unit: s + scope: + name: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/redisreceiver + version: latest diff --git a/receiver/redisreceiver/testdata/integration/redis-sentinel.conf b/receiver/redisreceiver/testdata/integration/redis-sentinel.conf new file mode 100644 index 0000000000000..c9686dd14ee91 --- /dev/null +++ b/receiver/redisreceiver/testdata/integration/redis-sentinel.conf @@ -0,0 +1,11 @@ +port 26379 +protected-mode no +daemonize no +dir /data + +# Define one monitor; it doesn't have to be reachable for Sentinel to run. +# quorum=1 keeps it simple. +sentinel monitor mymaster 127.0.0.1 6379 1 +sentinel down-after-milliseconds mymaster 5000 +sentinel parallel-syncs mymaster 1 +sentinel failover-timeout mymaster 10000