diff --git a/receiver/zookeeperreceiver/README.md b/receiver/zookeeperreceiver/README.md index 399382f44c9f..a7ef25837d94 100644 --- a/receiver/zookeeperreceiver/README.md +++ b/receiver/zookeeperreceiver/README.md @@ -23,5 +23,48 @@ receivers: collection_interval: 20s ``` +## Metrics + +Details about the metrics produced by this receiver can be found in [metadata.yaml](./metadata.yaml) with further documentation in [documentation.md](./documentation.md) + +### Feature gate configurations + +#### Transition from metrics with "direction" attribute + +Some zookeeper metrics reported are transitioning from being reported with + a `direction` attribute to being reported with the +direction included in the metric name to adhere to the OpenTelemetry specification +(https://github.com/open-telemetry/opentelemetry-specification/pull/2617): + +- `zookeeper.packet.count` will become: + - `zookeeper.packet.received.count` + - `zookeeper.packet.sent.count` + +The following feature gates control the transition process: + +- **receiver.zookeeperreceiver.emitMetricsWithoutDirectionAttribute**: controls if the new metrics without `direction` attribute are emitted by the receiver. +- **receiver.zookeeperreceiver.emitMetricsWithDirectionAttribute**: controls if the deprecated metrics with `direction` attribute are emitted by the receiver. + +##### Transition schedule: + +1. v0.57.0, July 2022: + +- The new metrics are available for all scrapers, but disabled by default, they can be enabled with the feature gates. +- The old metrics with `direction` attribute are deprecated with a warning. +- `receiver.zookeeperreceiver.emitMetricsWithDirectionAttribute` is enabled by default. +- `receiver.zookeeperreceiver.emitMetricsWithoutDirectionAttribute` is disabled by default. + +2. v0.58.0, August 2022: + +- The new metrics are enabled by default, deprecated metrics disabled, they can be enabled with the feature gates. +- `receiver.zookeeperreceiver.emitMetricsWithDirectionAttribute` is disabled by default. +- `receiver.zookeeperreceiver.emitMetricsWithoutDirectionAttribute` is enabled by default. + +3. v0.60.0, September 2022: + +- The feature gates are removed. +- The new metrics without `direction` attribute are always emitted. +- The deprecated metrics with `direction` attribute are no longer available. + [in development]: https://github.com/open-telemetry/opentelemetry-collector#in-development -[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib \ No newline at end of file +[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib diff --git a/receiver/zookeeperreceiver/documentation.md b/receiver/zookeeperreceiver/documentation.md index 9a70a8c9c12f..a1ccf19b0198 100644 --- a/receiver/zookeeperreceiver/documentation.md +++ b/receiver/zookeeperreceiver/documentation.md @@ -19,6 +19,8 @@ These are the metrics available for this scraper. | **zookeeper.latency.max** | Maximum time in milliseconds for requests to be processed. | ms | Gauge(Int) | | | **zookeeper.latency.min** | Minimum time in milliseconds for requests to be processed. | ms | Gauge(Int) | | | **zookeeper.packet.count** | The number of ZooKeeper packets received or sent by a server. | {packets} | Sum(Int) | | +| **zookeeper.packet.received.count** | The number of ZooKeeper packets received by a server. | {packets} | Sum(Int) | | +| **zookeeper.packet.sent.count** | The number of ZooKeeper packets sent by a server. | {packets} | Sum(Int) | | | **zookeeper.request.active** | Number of currently executing requests. | {requests} | Sum(Int) | | | **zookeeper.sync.pending** | The number of pending syncs from the followers. Only exposed by the leader. | {syncs} | Sum(Int) | | | **zookeeper.watch.count** | Number of watches placed on Z-Nodes on a ZooKeeper server. | {watches} | Sum(Int) | | diff --git a/receiver/zookeeperreceiver/internal/metadata/generated_metrics_v2.go b/receiver/zookeeperreceiver/internal/metadata/generated_metrics_v2.go index 89cf0d643e9b..95299c835c24 100644 --- a/receiver/zookeeperreceiver/internal/metadata/generated_metrics_v2.go +++ b/receiver/zookeeperreceiver/internal/metadata/generated_metrics_v2.go @@ -28,6 +28,8 @@ type MetricsSettings struct { ZookeeperLatencyMax MetricSettings `mapstructure:"zookeeper.latency.max"` ZookeeperLatencyMin MetricSettings `mapstructure:"zookeeper.latency.min"` ZookeeperPacketCount MetricSettings `mapstructure:"zookeeper.packet.count"` + ZookeeperPacketReceivedCount MetricSettings `mapstructure:"zookeeper.packet.received.count"` + ZookeeperPacketSentCount MetricSettings `mapstructure:"zookeeper.packet.sent.count"` ZookeeperRequestActive MetricSettings `mapstructure:"zookeeper.request.active"` ZookeeperSyncPending MetricSettings `mapstructure:"zookeeper.sync.pending"` ZookeeperWatchCount MetricSettings `mapstructure:"zookeeper.watch.count"` @@ -69,6 +71,12 @@ func DefaultMetricsSettings() MetricsSettings { ZookeeperPacketCount: MetricSettings{ Enabled: true, }, + ZookeeperPacketReceivedCount: MetricSettings{ + Enabled: true, + }, + ZookeeperPacketSentCount: MetricSettings{ + Enabled: true, + }, ZookeeperRequestActive: MetricSettings{ Enabled: true, }, @@ -693,6 +701,108 @@ func newMetricZookeeperPacketCount(settings MetricSettings) metricZookeeperPacke return m } +type metricZookeeperPacketReceivedCount 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 zookeeper.packet.received.count metric with initial data. +func (m *metricZookeeperPacketReceivedCount) init() { + m.data.SetName("zookeeper.packet.received.count") + m.data.SetDescription("The number of ZooKeeper packets received by a server.") + m.data.SetUnit("{packets}") + m.data.SetDataType(pmetric.MetricDataTypeSum) + m.data.Sum().SetIsMonotonic(true) + m.data.Sum().SetAggregationTemporality(pmetric.MetricAggregationTemporalityCumulative) +} + +func (m *metricZookeeperPacketReceivedCount) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.settings.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntVal(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricZookeeperPacketReceivedCount) 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 *metricZookeeperPacketReceivedCount) emit(metrics pmetric.MetricSlice) { + if m.settings.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricZookeeperPacketReceivedCount(settings MetricSettings) metricZookeeperPacketReceivedCount { + m := metricZookeeperPacketReceivedCount{settings: settings} + if settings.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricZookeeperPacketSentCount 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 zookeeper.packet.sent.count metric with initial data. +func (m *metricZookeeperPacketSentCount) init() { + m.data.SetName("zookeeper.packet.sent.count") + m.data.SetDescription("The number of ZooKeeper packets sent by a server.") + m.data.SetUnit("{packets}") + m.data.SetDataType(pmetric.MetricDataTypeSum) + m.data.Sum().SetIsMonotonic(true) + m.data.Sum().SetAggregationTemporality(pmetric.MetricAggregationTemporalityCumulative) +} + +func (m *metricZookeeperPacketSentCount) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.settings.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntVal(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricZookeeperPacketSentCount) 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 *metricZookeeperPacketSentCount) emit(metrics pmetric.MetricSlice) { + if m.settings.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricZookeeperPacketSentCount(settings MetricSettings) metricZookeeperPacketSentCount { + m := metricZookeeperPacketSentCount{settings: settings} + if settings.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + type metricZookeeperRequestActive struct { data pmetric.Metric // data buffer for generated metric. settings MetricSettings // metric settings provided by user. @@ -916,6 +1026,8 @@ type MetricsBuilder struct { metricZookeeperLatencyMax metricZookeeperLatencyMax metricZookeeperLatencyMin metricZookeeperLatencyMin metricZookeeperPacketCount metricZookeeperPacketCount + metricZookeeperPacketReceivedCount metricZookeeperPacketReceivedCount + metricZookeeperPacketSentCount metricZookeeperPacketSentCount metricZookeeperRequestActive metricZookeeperRequestActive metricZookeeperSyncPending metricZookeeperSyncPending metricZookeeperWatchCount metricZookeeperWatchCount @@ -948,6 +1060,8 @@ func NewMetricsBuilder(settings MetricsSettings, buildInfo component.BuildInfo, metricZookeeperLatencyMax: newMetricZookeeperLatencyMax(settings.ZookeeperLatencyMax), metricZookeeperLatencyMin: newMetricZookeeperLatencyMin(settings.ZookeeperLatencyMin), metricZookeeperPacketCount: newMetricZookeeperPacketCount(settings.ZookeeperPacketCount), + metricZookeeperPacketReceivedCount: newMetricZookeeperPacketReceivedCount(settings.ZookeeperPacketReceivedCount), + metricZookeeperPacketSentCount: newMetricZookeeperPacketSentCount(settings.ZookeeperPacketSentCount), metricZookeeperRequestActive: newMetricZookeeperRequestActive(settings.ZookeeperRequestActive), metricZookeeperSyncPending: newMetricZookeeperSyncPending(settings.ZookeeperSyncPending), metricZookeeperWatchCount: newMetricZookeeperWatchCount(settings.ZookeeperWatchCount), @@ -1029,6 +1143,8 @@ func (mb *MetricsBuilder) EmitForResource(rmo ...ResourceMetricsOption) { mb.metricZookeeperLatencyMax.emit(ils.Metrics()) mb.metricZookeeperLatencyMin.emit(ils.Metrics()) mb.metricZookeeperPacketCount.emit(ils.Metrics()) + mb.metricZookeeperPacketReceivedCount.emit(ils.Metrics()) + mb.metricZookeeperPacketSentCount.emit(ils.Metrics()) mb.metricZookeeperRequestActive.emit(ils.Metrics()) mb.metricZookeeperSyncPending.emit(ils.Metrics()) mb.metricZookeeperWatchCount.emit(ils.Metrics()) @@ -1107,6 +1223,16 @@ func (mb *MetricsBuilder) RecordZookeeperPacketCountDataPoint(ts pcommon.Timesta mb.metricZookeeperPacketCount.recordDataPoint(mb.startTime, ts, val, directionAttributeValue.String()) } +// RecordZookeeperPacketReceivedCountDataPoint adds a data point to zookeeper.packet.received.count metric. +func (mb *MetricsBuilder) RecordZookeeperPacketReceivedCountDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricZookeeperPacketReceivedCount.recordDataPoint(mb.startTime, ts, val) +} + +// RecordZookeeperPacketSentCountDataPoint adds a data point to zookeeper.packet.sent.count metric. +func (mb *MetricsBuilder) RecordZookeeperPacketSentCountDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricZookeeperPacketSentCount.recordDataPoint(mb.startTime, ts, val) +} + // RecordZookeeperRequestActiveDataPoint adds a data point to zookeeper.request.active metric. func (mb *MetricsBuilder) RecordZookeeperRequestActiveDataPoint(ts pcommon.Timestamp, val int64) { mb.metricZookeeperRequestActive.recordDataPoint(mb.startTime, ts, val) diff --git a/receiver/zookeeperreceiver/metadata.yaml b/receiver/zookeeperreceiver/metadata.yaml index 3778c6614287..e343c51240d0 100644 --- a/receiver/zookeeperreceiver/metadata.yaml +++ b/receiver/zookeeperreceiver/metadata.yaml @@ -118,6 +118,7 @@ metrics: unit: "{file_descriptors}" gauge: value_type: int + # produced when receiver.zookeeperreceiver.emitMetricsWithDirectionAttribute feature gate is enabled zookeeper.packet.count: enabled: true description: The number of ZooKeeper packets received or sent by a server. @@ -127,6 +128,24 @@ metrics: value_type: int monotonic: true aggregation: cumulative + # produced when receiver.zookeeperreceiver.emitMetricsWithoutDirectionAttribute feature gate is enabled + zookeeper.packet.sent.count: + enabled: true + description: The number of ZooKeeper packets sent by a server. + unit: "{packets}" + sum: + value_type: int + monotonic: true + aggregation: cumulative + # produced when receiver.zookeeperreceiver.emitMetricsWithoutDirectionAttribute feature gate is enabled + zookeeper.packet.received.count: + enabled: true + description: The number of ZooKeeper packets received by a server. + unit: "{packets}" + sum: + value_type: int + monotonic: true + aggregation: cumulative zookeeper.fsync.exceeded_threshold.count: enabled: true description: Number of times fsync duration has exceeded warning threshold. diff --git a/receiver/zookeeperreceiver/metrics.go b/receiver/zookeeperreceiver/metrics.go index 0aeb045a5ea9..4759f990ebe1 100644 --- a/receiver/zookeeperreceiver/metrics.go +++ b/receiver/zookeeperreceiver/metrics.go @@ -53,12 +53,17 @@ const ( type metricCreator struct { computedMetricStore map[string]int64 mb *metadata.MetricsBuilder + // Temporary feature gates while transitioning to metrics without a direction attribute + emitMetricsWithDirectionAttribute bool + emitMetricsWithoutDirectionAttribute bool } -func newMetricCreator(mb *metadata.MetricsBuilder) *metricCreator { +func newMetricCreator(mb *metadata.MetricsBuilder, emitMetricsWithDirectionAttribute, emitMetricsWithoutDirectionAttribute bool) *metricCreator { return &metricCreator{ - computedMetricStore: make(map[string]int64), - mb: mb, + computedMetricStore: make(map[string]int64), + mb: mb, + emitMetricsWithDirectionAttribute: emitMetricsWithDirectionAttribute, + emitMetricsWithoutDirectionAttribute: emitMetricsWithoutDirectionAttribute, } } @@ -101,11 +106,21 @@ func (m *metricCreator) recordDataPointsFunc(metric string) func(ts pcommon.Time return m.mb.RecordZookeeperFsyncExceededThresholdCountDataPoint case packetsReceivedMetricKey: return func(ts pcommon.Timestamp, val int64) { - m.mb.RecordZookeeperPacketCountDataPoint(ts, val, metadata.AttributeDirectionReceived) + if m.emitMetricsWithDirectionAttribute { + m.mb.RecordZookeeperPacketCountDataPoint(ts, val, metadata.AttributeDirectionReceived) + } + if m.emitMetricsWithoutDirectionAttribute { + m.mb.RecordZookeeperPacketReceivedCountDataPoint(ts, val) + } } case packetsSentMetricKey: return func(ts pcommon.Timestamp, val int64) { - m.mb.RecordZookeeperPacketCountDataPoint(ts, val, metadata.AttributeDirectionSent) + if m.emitMetricsWithDirectionAttribute { + m.mb.RecordZookeeperPacketCountDataPoint(ts, val, metadata.AttributeDirectionSent) + } + if m.emitMetricsWithoutDirectionAttribute { + m.mb.RecordZookeeperPacketSentCountDataPoint(ts, val) + } } } diff --git a/receiver/zookeeperreceiver/scraper.go b/receiver/zookeeperreceiver/scraper.go index 69e18d1dfd28..83c72d1aa334 100644 --- a/receiver/zookeeperreceiver/scraper.go +++ b/receiver/zookeeperreceiver/scraper.go @@ -27,6 +27,7 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/service/featuregate" "go.uber.org/zap" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zookeeperreceiver/internal/metadata" @@ -35,9 +36,38 @@ import ( var zookeeperFormatRE = regexp.MustCompile(`(^zk_\w+)\s+([\w\.\-]+)`) const ( - mntrCommand = "mntr" + mntrCommand = "mntr" + emitMetricsWithDirectionAttributeFeatureGateID = "receiver.zookeeperreceiver.emitMetricsWithDirectionAttribute" + emitMetricsWithoutDirectionAttributeFeatureGateID = "receiver.zookeeperreceiver.emitMetricsWithoutDirectionAttribute" ) +var ( + emitMetricsWithDirectionAttributeFeatureGate = featuregate.Gate{ + ID: emitMetricsWithDirectionAttributeFeatureGateID, + Enabled: true, + Description: "Some zookeeper metrics reported are transitioning from being reported with a direction " + + "attribute to being reported with the direction included in the metric name to adhere to the " + + "OpenTelemetry specification. This feature gate controls emitting the old metrics with the direction " + + "attribute. For more details, see: " + + "https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/zookeeperreceiver/README.md#feature-gate-configurations", + } + + emitMetricsWithoutDirectionAttributeFeatureGate = featuregate.Gate{ + ID: emitMetricsWithoutDirectionAttributeFeatureGateID, + Enabled: false, + Description: "Some zookeeper metrics reported are transitioning from being reported with a direction " + + "attribute to being reported with the direction included in the metric name to adhere to the " + + "OpenTelemetry specification. This feature gate controls emitting the new metrics without the direction " + + "attribute. For more details, see: " + + "https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/zookeeperreceiver/README.md#feature-gate-configurations", + } +) + +func init() { + featuregate.GetRegistry().MustRegister(emitMetricsWithDirectionAttributeFeatureGate) + featuregate.GetRegistry().MustRegister(emitMetricsWithoutDirectionAttributeFeatureGate) +} + type zookeeperMetricsScraper struct { logger *zap.Logger config *Config @@ -48,6 +78,10 @@ type zookeeperMetricsScraper struct { closeConnection func(net.Conn) error setConnectionDeadline func(net.Conn, time.Time) error sendCmd func(net.Conn, string) (*bufio.Scanner, error) + + // Feature gates while transitioning to metrics without a direction attribute + emitMetricsWithDirectionAttribute bool + emitMetricsWithoutDirectionAttribute bool } func (z *zookeeperMetricsScraper) Name() string { @@ -64,14 +98,29 @@ func newZookeeperMetricsScraper(settings component.ReceiverCreateSettings, confi return nil, errors.New("timeout must be a positive duration") } - return &zookeeperMetricsScraper{ - logger: settings.Logger, - config: config, - mb: metadata.NewMetricsBuilder(config.Metrics, settings.BuildInfo), - closeConnection: closeConnection, - setConnectionDeadline: setConnectionDeadline, - sendCmd: sendCmd, - }, nil + z := &zookeeperMetricsScraper{ + logger: settings.Logger, + config: config, + mb: metadata.NewMetricsBuilder(config.Metrics, settings.BuildInfo), + closeConnection: closeConnection, + setConnectionDeadline: setConnectionDeadline, + sendCmd: sendCmd, + emitMetricsWithDirectionAttribute: featuregate.GetRegistry().IsEnabled(emitMetricsWithDirectionAttributeFeatureGateID), + emitMetricsWithoutDirectionAttribute: featuregate.GetRegistry().IsEnabled(emitMetricsWithoutDirectionAttributeFeatureGateID), + } + + if z.emitMetricsWithDirectionAttribute { + z.logger.Info("WARNING - Breaking Change: " + emitMetricsWithDirectionAttributeFeatureGate.Description) + z.logger.Info("The feature gate " + emitMetricsWithDirectionAttributeFeatureGate.ID + " is enabled. This " + + "otel collector will report metrics with a direction attribute, be aware this will not be supported in the future") + } + + if z.emitMetricsWithoutDirectionAttribute { + z.logger.Info("The " + emitMetricsWithoutDirectionAttributeFeatureGate.ID + " feature gate is enabled. This " + + "otel collector will report metrics without a direction attribute, which is good for future support") + } + + return z, nil } func (z *zookeeperMetricsScraper) shutdown(_ context.Context) error { @@ -120,7 +169,7 @@ func (z *zookeeperMetricsScraper) getResourceMetrics(conn net.Conn) (pmetric.Met return pmetric.NewMetrics(), err } - creator := newMetricCreator(z.mb) + creator := newMetricCreator(z.mb, z.emitMetricsWithDirectionAttribute, z.emitMetricsWithoutDirectionAttribute) now := pcommon.NewTimestampFromTime(time.Now()) resourceOpts := make([]metadata.ResourceMetricsOption, 0, 2) for scanner.Scan() { diff --git a/receiver/zookeeperreceiver/scraper_test.go b/receiver/zookeeperreceiver/scraper_test.go index 2039942470a4..a5d28f10d231 100644 --- a/receiver/zookeeperreceiver/scraper_test.go +++ b/receiver/zookeeperreceiver/scraper_test.go @@ -29,6 +29,7 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/service/featuregate" "go.uber.org/zap" "go.uber.org/zap/zapcore" "go.uber.org/zap/zaptest/observer" @@ -49,19 +50,42 @@ func TestZookeeperMetricsScraperScrape(t *testing.T) { t.Skip("skipping flaky test on windows, see https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/10171") } + // additional temporary log messages expected while transitioning to metrics without a direction attribute + expectedLogsWithDirectionAttribute := []logMsg{ + { + msg: "WARNING - Breaking Change: " + emitMetricsWithDirectionAttributeFeatureGate.Description, + level: zapcore.InfoLevel, + }, + { + msg: "The feature gate " + emitMetricsWithDirectionAttributeFeatureGate.ID + " is enabled. This " + + "otel collector will report metrics with a direction attribute, be aware this will not be supported in the future", + level: zapcore.InfoLevel, + }, + } + + expectedLogsWithoutDirectionAttribute := []logMsg{ + { + msg: "The " + emitMetricsWithoutDirectionAttributeFeatureGate.ID + " feature gate is enabled. This " + + "otel collector will report metrics without a direction attribute, which is good for future support", + level: zapcore.InfoLevel, + }, + } + tests := []struct { - name string - expectedMetricsFilename string - expectedResourceAttributes map[string]string - metricsSettings func() metadata.MetricsSettings - mockedZKOutputSourceFilename string - mockZKConnectionErr bool - expectedLogs []logMsg - expectedNumResourceMetrics int - setConnectionDeadline func(net.Conn, time.Time) error - closeConnection func(net.Conn) error - sendCmd func(net.Conn, string) (*bufio.Scanner, error) - wantErr bool + name string + expectedMetricsFilename string + expectedResourceAttributes map[string]string + metricsSettings func() metadata.MetricsSettings + mockedZKOutputSourceFilename string + mockZKConnectionErr bool + expectedLogs []logMsg + expectedNumResourceMetrics int + setConnectionDeadline func(net.Conn, time.Time) error + closeConnection func(net.Conn) error + sendCmd func(net.Conn, string) (*bufio.Scanner, error) + wantErr bool + emitMetricsWithDirectionAttribute bool + emitMetricsWithoutDirectionAttribute bool }{ { name: "Test correctness with v3.4.14", @@ -77,7 +101,27 @@ func TestZookeeperMetricsScraperScrape(t *testing.T) { level: zapcore.DebugLevel, }, }, - expectedNumResourceMetrics: 1, + expectedNumResourceMetrics: 1, + emitMetricsWithDirectionAttribute: true, + emitMetricsWithoutDirectionAttribute: false, + }, + { + name: "Test correctness with v3.4.14 without direction attribute", + mockedZKOutputSourceFilename: "mntr-3.4.14", + expectedMetricsFilename: "correctness-v3.4.14-without-direction", + expectedResourceAttributes: map[string]string{ + "server.state": "standalone", + "zk.version": "3.4.14-4c25d480e66aadd371de8bd2fd8da255ac140bcf", + }, + expectedLogs: []logMsg{ + { + msg: "metric computation failed", + level: zapcore.DebugLevel, + }, + }, + expectedNumResourceMetrics: 1, + emitMetricsWithDirectionAttribute: false, + emitMetricsWithoutDirectionAttribute: true, }, { name: "Test correctness with v3.5.5", @@ -87,7 +131,21 @@ func TestZookeeperMetricsScraperScrape(t *testing.T) { "server.state": "leader", "zk.version": "3.5.5-390fe37ea45dee01bf87dc1c042b5e3dcce88653", }, - expectedNumResourceMetrics: 1, + expectedNumResourceMetrics: 1, + emitMetricsWithDirectionAttribute: true, + emitMetricsWithoutDirectionAttribute: false, + }, + { + name: "Test correctness with v3.5.5 without direction attribute", + mockedZKOutputSourceFilename: "mntr-3.5.5", + expectedMetricsFilename: "correctness-v3.5.5-without-direction", + expectedResourceAttributes: map[string]string{ + "server.state": "leader", + "zk.version": "3.5.5-390fe37ea45dee01bf87dc1c042b5e3dcce88653", + }, + expectedNumResourceMetrics: 1, + emitMetricsWithDirectionAttribute: false, + emitMetricsWithoutDirectionAttribute: true, }, { name: "Arbitrary connection error", @@ -152,6 +210,8 @@ func TestZookeeperMetricsScraperScrape(t *testing.T) { setConnectionDeadline: func(conn net.Conn, t time.Time) error { return errors.New("") }, + emitMetricsWithDirectionAttribute: true, + emitMetricsWithoutDirectionAttribute: false, }, { name: "Error closing connection", @@ -175,6 +235,8 @@ func TestZookeeperMetricsScraperScrape(t *testing.T) { closeConnection: func(conn net.Conn) error { return errors.New("") }, + emitMetricsWithDirectionAttribute: true, + emitMetricsWithoutDirectionAttribute: false, }, { name: "Failed to send command", @@ -208,7 +270,9 @@ func TestZookeeperMetricsScraperScrape(t *testing.T) { level: zapcore.DebugLevel, }, }, - expectedNumResourceMetrics: 1, + expectedNumResourceMetrics: 1, + emitMetricsWithDirectionAttribute: true, + emitMetricsWithoutDirectionAttribute: false, }, } for _, tt := range tests { @@ -226,6 +290,8 @@ func TestZookeeperMetricsScraperScrape(t *testing.T) { cfg.Metrics = tt.metricsSettings() } + featuregate.GetRegistry().MustApply(map[string]bool{emitMetricsWithDirectionAttributeFeatureGate.ID: tt.emitMetricsWithDirectionAttribute}) + featuregate.GetRegistry().MustApply(map[string]bool{emitMetricsWithoutDirectionAttributeFeatureGate.ID: tt.emitMetricsWithoutDirectionAttribute}) core, observedLogs := observer.New(zap.DebugLevel) settings := componenttest.NewNopReceiverCreateSettings() settings.Logger = zap.New(core) @@ -250,8 +316,20 @@ func TestZookeeperMetricsScraperScrape(t *testing.T) { actualMetrics, err := z.scrape(ctx) require.NoError(t, z.shutdown(ctx)) - require.Equal(t, len(tt.expectedLogs), observedLogs.Len()) - for i, log := range tt.expectedLogs { + var expectedLogs []logMsg + + if tt.emitMetricsWithoutDirectionAttribute { + expectedLogs = append(expectedLogs, expectedLogsWithoutDirectionAttribute...) + } + + if tt.emitMetricsWithDirectionAttribute { + expectedLogs = append(expectedLogs, expectedLogsWithDirectionAttribute...) + } + + expectedLogs = append(expectedLogs, tt.expectedLogs...) + + require.Equal(t, len(expectedLogs), observedLogs.Len()) + for i, log := range expectedLogs { require.Equal(t, log.msg, observedLogs.All()[i].Message) require.Equal(t, log.level, observedLogs.All()[i].Level) } diff --git a/receiver/zookeeperreceiver/testdata/scraper/correctness-v3.4.14-without-direction.json b/receiver/zookeeperreceiver/testdata/scraper/correctness-v3.4.14-without-direction.json new file mode 100644 index 000000000000..6774cdf4a347 --- /dev/null +++ b/receiver/zookeeperreceiver/testdata/scraper/correctness-v3.4.14-without-direction.json @@ -0,0 +1,242 @@ +{ + "resourceMetrics": [ + { + "scopeMetrics": [ + { + "scope": { + "name": "otelcol/zookeeperreceiver", + "version": "latest" + }, + "metrics": [ + { + "description": "Number of active clients connected to a ZooKeeper server.", + "name": "zookeeper.connection.active", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "1", + "startTimeUnixNano": "1642685966444471000", + "timeUnixNano": "1642685966444739000" + } + ] + }, + "unit": "{connections}" + }, + { + "description": "Number of ephemeral nodes that a ZooKeeper server has in its data tree.", + "name": "zookeeper.data_tree.ephemeral_node.count", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "0", + "startTimeUnixNano": "1642685966444471000", + "timeUnixNano": "1642685966444739000" + } + ] + }, + "unit": "{nodes}" + }, + { + "description": "Size of data in bytes that a ZooKeeper server has in its data tree.", + "name": "zookeeper.data_tree.size", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "27", + "startTimeUnixNano": "1642685966444471000", + "timeUnixNano": "1642685966444739000" + } + ] + }, + "unit": "By" + }, + { + "description": "Maximum number of file descriptors that a ZooKeeper server can open.", + "gauge": { + "dataPoints": [ + { + "asInt": "1048576", + "startTimeUnixNano": "1642685966444471000", + "timeUnixNano": "1642685966444739000" + } + ] + }, + "name": "zookeeper.file_descriptor.limit", + "unit": "{file_descriptors}" + }, + { + "description": "Number of file descriptors that a ZooKeeper server has open.", + "name": "zookeeper.file_descriptor.open", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "26", + "startTimeUnixNano": "1642685966444471000", + "timeUnixNano": "1642685966444739000" + } + ] + }, + "unit": "{file_descriptors}" + }, + { + "description": "Number of times fsync duration has exceeded warning threshold.", + "name": "zookeeper.fsync.exceeded_threshold.count", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "0", + "startTimeUnixNano": "1642685966444471000", + "timeUnixNano": "1642685966444739000" + } + ], + "isMonotonic": true + }, + "unit": "{events}" + }, + { + "description": "Average time in milliseconds for requests to be processed.", + "gauge": { + "dataPoints": [ + { + "asInt": "0", + "startTimeUnixNano": "1642685966444471000", + "timeUnixNano": "1642685966444739000" + } + ] + }, + "name": "zookeeper.latency.avg", + "unit": "ms" + }, + { + "description": "Maximum time in milliseconds for requests to be processed.", + "gauge": { + "dataPoints": [ + { + "asInt": "0", + "startTimeUnixNano": "1642685966444471000", + "timeUnixNano": "1642685966444739000" + } + ] + }, + "name": "zookeeper.latency.max", + "unit": "ms" + }, + { + "description": "Minimum time in milliseconds for requests to be processed.", + "gauge": { + "dataPoints": [ + { + "asInt": "0", + "startTimeUnixNano": "1642685966444471000", + "timeUnixNano": "1642685966444739000" + } + ] + }, + "name": "zookeeper.latency.min", + "unit": "ms" + }, + { + "description": "The number of ZooKeeper packets received by a server.", + "name": "zookeeper.packet.received.count", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "1", + + "startTimeUnixNano": "1642685966444471000", + "timeUnixNano": "1642685966444739000" + } + ], + "isMonotonic": true + }, + "unit": "{packets}" + }, + { + "description": "The number of ZooKeeper packets sent by a server.", + "name": "zookeeper.packet.sent.count", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "0", + "startTimeUnixNano": "1642685966444471000", + "timeUnixNano": "1642685966444739000" + } + ], + "isMonotonic": true + }, + "unit": "{packets}" + }, + { + "description": "Number of currently executing requests.", + "name": "zookeeper.request.active", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "0", + "startTimeUnixNano": "1642685966444471000", + "timeUnixNano": "1642685966444739000" + } + ] + }, + "unit": "{requests}" + }, + { + "description": "Number of watches placed on Z-Nodes on a ZooKeeper server.", + "name": "zookeeper.watch.count", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "0", + "startTimeUnixNano": "1642685966444471000", + "timeUnixNano": "1642685966444739000" + } + ] + }, + "unit": "{watches}" + }, + { + "description": "Number of z-nodes that a ZooKeeper server has in its data tree.", + "name": "zookeeper.znode.count", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "4", + "startTimeUnixNano": "1642685966444471000", + "timeUnixNano": "1642685966444739000" + } + ] + }, + "unit": "{znodes}" + } + ] + } + ], + "resource": { + "attributes": [ + { + "key": "zk.version", + "value": { + "stringValue": "3.4.14-4c25d480e66aadd371de8bd2fd8da255ac140bcf" + } + }, + { + "key": "server.state", + "value": { + "stringValue": "standalone" + } + } + ] + } + } + ] +} diff --git a/receiver/zookeeperreceiver/testdata/scraper/correctness-v3.5.5-without-direction.json b/receiver/zookeeperreceiver/testdata/scraper/correctness-v3.5.5-without-direction.json new file mode 100644 index 000000000000..3a8b0f927d5d --- /dev/null +++ b/receiver/zookeeperreceiver/testdata/scraper/correctness-v3.5.5-without-direction.json @@ -0,0 +1,276 @@ +{ + "resourceMetrics": [ + { + "scopeMetrics": [ + { + "scope": { + "name": "otelcol/zookeeperreceiver", + "version": "latest" + }, + "metrics": [ + { + "description": "Number of active clients connected to a ZooKeeper server.", + "name": "zookeeper.connection.active", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "1", + "startTimeUnixNano": "1642685966447704000", + "timeUnixNano": "1642685966447860000" + } + ] + }, + "unit": "{connections}" + }, + { + "description": "Number of ephemeral nodes that a ZooKeeper server has in its data tree.", + "name": "zookeeper.data_tree.ephemeral_node.count", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "0", + "startTimeUnixNano": "1642685966447704000", + "timeUnixNano": "1642685966447860000" + } + ] + }, + "unit": "{nodes}" + }, + { + "description": "Size of data in bytes that a ZooKeeper server has in its data tree.", + "name": "zookeeper.data_tree.size", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "107", + "startTimeUnixNano": "1642685966447704000", + "timeUnixNano": "1642685966447860000" + } + ] + }, + "unit": "By" + }, + { + "description": "Maximum number of file descriptors that a ZooKeeper server can open.", + "gauge": { + "dataPoints": [ + { + "asInt": "1048576", + "startTimeUnixNano": "1642685966447704000", + "timeUnixNano": "1642685966447860000" + } + ] + }, + "name": "zookeeper.file_descriptor.limit", + "unit": "{file_descriptors}" + }, + { + "description": "Number of file descriptors that a ZooKeeper server has open.", + "name": "zookeeper.file_descriptor.open", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "54", + "startTimeUnixNano": "1642685966447704000", + "timeUnixNano": "1642685966447860000" + } + ] + }, + "unit": "{file_descriptors}" + }, + { + "description": "The number of followers. Only exposed by the leader.", + "name": "zookeeper.follower.count", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "1", + "attributes": [ + { + "key": "state", + "value": { + "stringValue": "synced" + } + } + ], + "startTimeUnixNano": "1642685966447704000", + "timeUnixNano": "1642685966447860000" + }, + { + "asInt": "1", + "attributes": [ + { + "key": "state", + "value": { + "stringValue": "unsynced" + } + } + ], + "startTimeUnixNano": "1642685966447704000", + "timeUnixNano": "1642685966447860000" + } + ] + }, + "unit": "{followers}" + }, + { + "description": "Average time in milliseconds for requests to be processed.", + "gauge": { + "dataPoints": [ + { + "asInt": "0", + "startTimeUnixNano": "1642685966447704000", + "timeUnixNano": "1642685966447860000" + } + ] + }, + "name": "zookeeper.latency.avg", + "unit": "ms" + }, + { + "description": "Maximum time in milliseconds for requests to be processed.", + "gauge": { + "dataPoints": [ + { + "asInt": "0", + "startTimeUnixNano": "1642685966447704000", + "timeUnixNano": "1642685966447860000" + } + ] + }, + "name": "zookeeper.latency.max", + "unit": "ms" + }, + { + "description": "Minimum time in milliseconds for requests to be processed.", + "gauge": { + "dataPoints": [ + { + "asInt": "0", + "startTimeUnixNano": "1642685966447704000", + "timeUnixNano": "1642685966447860000" + } + ] + }, + "name": "zookeeper.latency.min", + "unit": "ms" + }, + { + "description": "The number of ZooKeeper packets received by a server.", + "name": "zookeeper.packet.received.count", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "1", + "startTimeUnixNano": "1642685966447704000", + "timeUnixNano": "1642685966447860000" + } + ], + "isMonotonic": true + }, + "unit": "{packets}" + }, + { + "description": "The number of ZooKeeper packets sent by a server.", + "name": "zookeeper.packet.sent.count", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "0", + "startTimeUnixNano": "1642685966447704000", + "timeUnixNano": "1642685966447860000" + } + ], + "isMonotonic": true + }, + "unit": "{packets}" + }, + { + "description": "Number of currently executing requests.", + "name": "zookeeper.request.active", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "0", + "startTimeUnixNano": "1642685966447704000", + "timeUnixNano": "1642685966447860000" + } + ] + }, + "unit": "{requests}" + }, + { + "description": "The number of pending syncs from the followers. Only exposed by the leader.", + "name": "zookeeper.sync.pending", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "0", + "startTimeUnixNano": "1642685966447704000", + "timeUnixNano": "1642685966447860000" + } + ] + }, + "unit": "{syncs}" + }, + { + "description": "Number of watches placed on Z-Nodes on a ZooKeeper server.", + "name": "zookeeper.watch.count", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "0", + "startTimeUnixNano": "1642685966447704000", + "timeUnixNano": "1642685966447860000" + } + ] + }, + "unit": "{watches}" + }, + { + "description": "Number of z-nodes that a ZooKeeper server has in its data tree.", + "name": "zookeeper.znode.count", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "dataPoints": [ + { + "asInt": "5", + "startTimeUnixNano": "1642685966447704000", + "timeUnixNano": "1642685966447860000" + } + ] + }, + "unit": "{znodes}" + } + ] + } + ], + "resource": { + "attributes": [ + { + "key": "zk.version", + "value": { + "stringValue": "3.5.5-390fe37ea45dee01bf87dc1c042b5e3dcce88653" + } + }, + { + "key": "server.state", + "value": { + "stringValue": "leader" + } + } + ] + } + } + ] +} diff --git a/unreleased/zk_direction.yaml b/unreleased/zk_direction.yaml new file mode 100755 index 000000000000..0f77d5dd69b2 --- /dev/null +++ b/unreleased/zk_direction.yaml @@ -0,0 +1,20 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: breaking + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: zookeeperreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: "Remove direction for metrics. The feature gate: receiver.zookeeperreceiver.emitMetricsWithoutDirectionAttribute can be set to apply the following (#12772)" + +# One or more tracking issues related to the change +issues: [12184] + +# (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: |- + - `zookeeper` metrics: + - `zookeeper.packet.count` will become: + - `zookeeper.packet.received.count` + - `zookeeper.packet.sent.count`