From 6206b695c6a0b5abc1105902c037958901121329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Fri, 15 Nov 2024 21:27:28 +0100 Subject: [PATCH] smb: refactor collector (#1740) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/collector/smb/const.go | 6 + internal/collector/smb/smb.go | 114 ++++++++---------- internal/collector/smbclient/const.go | 25 ++++ internal/collector/smbclient/smbclient.go | 138 ++++++++++------------ 4 files changed, 147 insertions(+), 136 deletions(-) create mode 100644 internal/collector/smb/const.go create mode 100644 internal/collector/smbclient/const.go diff --git a/internal/collector/smb/const.go b/internal/collector/smb/const.go new file mode 100644 index 00000000..6fb9829d --- /dev/null +++ b/internal/collector/smb/const.go @@ -0,0 +1,6 @@ +package smb + +const ( + currentOpenFileCount = "Current Open File Count" + treeConnectCount = "Tree Connect Count" +) diff --git a/internal/collector/smb/smb.go b/internal/collector/smb/smb.go index 8a1d212c..98abaff0 100644 --- a/internal/collector/smb/smb.go +++ b/internal/collector/smb/smb.go @@ -3,12 +3,14 @@ package smb import ( + "errors" + "fmt" "log/slog" - "strings" "github.com/alecthomas/kingpin/v2" "github.com/prometheus-community/windows_exporter/internal/mi" - v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" + "github.com/prometheus-community/windows_exporter/internal/perfdata" + "github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" ) @@ -22,6 +24,8 @@ var ConfigDefaults = Config{} type Collector struct { config Config + perfDataCollector perfdata.Collector + treeConnectCount *prometheus.Desc currentOpenFileCount *prometheus.Desc } @@ -47,89 +51,73 @@ func (c *Collector) GetName() string { } func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) { - return []string{ - "SMB Server Shares", - }, nil + return []string{}, nil } func (c *Collector) Close(_ *slog.Logger) error { + c.perfDataCollector.Close() + return nil } func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { - // desc creates a new prometheus description - desc := func(metricName string, description string, labels ...string) *prometheus.Desc { - return prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, "smb", metricName), - description, - labels, - nil, - ) + var err error + + c.perfDataCollector, err = perfdata.NewCollector(perfdata.V2, "SMB Server Shares", nil, []string{ + currentOpenFileCount, + treeConnectCount, + }) + if err != nil { + return fmt.Errorf("failed to create SMB Server Shares collector: %w", err) } - c.currentOpenFileCount = desc("server_shares_current_open_file_count", "Current total count open files on the SMB Server") - c.treeConnectCount = desc("server_shares_tree_connect_count", "Count of user connections to the SMB Server") + c.currentOpenFileCount = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "server_shares_current_open_file_count"), + "Current total count open files on the SMB Server", + nil, + nil, + ) + c.treeConnectCount = prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, Name, "server_shares_tree_connect_count"), + "Count of user connections to the SMB Server", + nil, + nil, + ) return nil } // Collect collects smb metrics and sends them to prometheus. -func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error { - logger = logger.With(slog.String("collector", Name)) - if err := c.collectServerShares(ctx, logger, ch); err != nil { - logger.Error("failed to collect server share metrics", - slog.Any("err", err), - ) - - return err +func (c *Collector) Collect(_ *types.ScrapeContext, _ *slog.Logger, ch chan<- prometheus.Metric) error { + if err := c.collectServerShares(ch); err != nil { + return fmt.Errorf("failed to collect server share metrics: %w", err) } return nil } -// Perflib: SMB Server Shares. -type perflibServerShares struct { - Name string - - CurrentOpenFileCount float64 `perflib:"Current Open File Count"` - TreeConnectCount float64 `perflib:"Tree Connect Count"` -} - -func (c *Collector) collectServerShares(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error { - logger = logger.With(slog.String("collector", Name)) - - var data []perflibServerShares - - if err := v1.UnmarshalObject(ctx.PerfObjects["SMB Server Shares"], &data, logger); err != nil { - return err +func (c *Collector) collectServerShares(ch chan<- prometheus.Metric) error { + perfData, err := c.perfDataCollector.Collect() + if err != nil { + return fmt.Errorf("failed to collect SMB Server Shares metrics: %w", err) } - for _, instance := range data { - labelName := c.toLabelName(instance.Name) - if !strings.HasSuffix(labelName, "_total") { - continue - } - - ch <- prometheus.MustNewConstMetric( - c.currentOpenFileCount, - prometheus.CounterValue, - instance.CurrentOpenFileCount, - ) - - ch <- prometheus.MustNewConstMetric( - c.treeConnectCount, - prometheus.CounterValue, - instance.TreeConnectCount, - ) + data, ok := perfData[perftypes.EmptyInstance] + if !ok { + return errors.New("query for SMB Server Shares returned empty result set") } + ch <- prometheus.MustNewConstMetric( + c.currentOpenFileCount, + prometheus.CounterValue, + data[currentOpenFileCount].FirstValue, + ) + + ch <- prometheus.MustNewConstMetric( + c.treeConnectCount, + prometheus.CounterValue, + data[treeConnectCount].FirstValue, + ) + return nil } - -// toLabelName converts strings to lowercase and replaces all whitespaces and dots with underscores. -func (c *Collector) toLabelName(name string) string { - s := strings.ReplaceAll(strings.Join(strings.Fields(strings.ToLower(name)), "_"), ".", "_") - s = strings.ReplaceAll(s, "__", "_") - - return s -} diff --git a/internal/collector/smbclient/const.go b/internal/collector/smbclient/const.go new file mode 100644 index 00000000..9075de58 --- /dev/null +++ b/internal/collector/smbclient/const.go @@ -0,0 +1,25 @@ +package smbclient + +const ( + AvgDataQueueLength = "Avg. Data Queue Length" + AvgReadQueueLength = "Avg. Read Queue Length" + AvgSecPerRead = "Avg. sec/Read" + AvgSecPerWrite = "Avg. sec/Write" + AvgSecPerDataRequest = "Avg. sec/Data Request" + AvgWriteQueueLength = "Avg. Write Queue Length" + CreditStallsPerSec = "Credit Stalls/sec" + CurrentDataQueueLength = "Current Data Queue Length" + DataBytesPerSec = "Data Bytes/sec" + DataRequestsPerSec = "Data Requests/sec" + MetadataRequestsPerSec = "Metadata Requests/sec" + ReadBytesTransmittedViaSMBDirectPerSec = "Read Bytes transmitted via SMB Direct/sec" + ReadBytesPerSec = "Read Bytes/sec" + ReadRequestsTransmittedViaSMBDirectPerSec = "Read Requests transmitted via SMB Direct/sec" + ReadRequestsPerSec = "Read Requests/sec" + TurboIOReadsPerSec = "Turbo I/O Reads/sec" + TurboIOWritesPerSec = "Turbo I/O Writes/sec" + WriteBytesTransmittedViaSMBDirectPerSec = "Write Bytes transmitted via SMB Direct/sec" + WriteBytesPerSec = "Write Bytes/sec" + WriteRequestsTransmittedViaSMBDirectPerSec = "Write Requests transmitted via SMB Direct/sec" + WriteRequestsPerSec = "Write Requests/sec" +) diff --git a/internal/collector/smbclient/smbclient.go b/internal/collector/smbclient/smbclient.go index ad5bde41..1bd343fe 100644 --- a/internal/collector/smbclient/smbclient.go +++ b/internal/collector/smbclient/smbclient.go @@ -3,13 +3,14 @@ package smbclient import ( + "fmt" "log/slog" "strings" "github.com/alecthomas/kingpin/v2" "github.com/prometheus-community/windows_exporter/internal/mi" + "github.com/prometheus-community/windows_exporter/internal/perfdata" "github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes" - v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" ) @@ -25,6 +26,8 @@ var ConfigDefaults = Config{} type Collector struct { config Config + perfDataCollector perfdata.Collector + readBytesTotal *prometheus.Desc readBytesTransmittedViaSMBDirectTotal *prometheus.Desc readRequestQueueSecsTotal *prometheus.Desc @@ -70,9 +73,7 @@ func (c *Collector) GetName() string { } func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) { - return []string{ - "SMB Client Shares", - }, nil + return []string{}, nil } func (c *Collector) Close(_ *slog.Logger) error { @@ -80,10 +81,39 @@ func (c *Collector) Close(_ *slog.Logger) error { } func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { + var err error + + c.perfDataCollector, err = perfdata.NewCollector(perfdata.V2, "SMB Client Shares", nil, []string{ + AvgDataQueueLength, + AvgReadQueueLength, + AvgSecPerRead, + AvgSecPerWrite, + AvgSecPerDataRequest, + AvgWriteQueueLength, + CreditStallsPerSec, + CurrentDataQueueLength, + DataBytesPerSec, + DataRequestsPerSec, + MetadataRequestsPerSec, + ReadBytesTransmittedViaSMBDirectPerSec, + ReadBytesPerSec, + ReadRequestsTransmittedViaSMBDirectPerSec, + ReadRequestsPerSec, + TurboIOReadsPerSec, + TurboIOWritesPerSec, + WriteBytesTransmittedViaSMBDirectPerSec, + WriteBytesPerSec, + WriteRequestsTransmittedViaSMBDirectPerSec, + WriteRequestsPerSec, + }) + if err != nil { + return fmt.Errorf("failed to create SMB Client Shares collector: %w", err) + } + // desc creates a new prometheus description desc := func(metricName string, description string, labels []string) *prometheus.Desc { return prometheus.NewDesc( - prometheus.BuildFQName(types.Namespace, "smbclient", metricName), + prometheus.BuildFQName(types.Namespace, Name, metricName), description, labels, nil, @@ -179,68 +209,30 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { } // Collect collects smb client metrics and sends them to prometheus. -func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error { - logger = logger.With(slog.String("collector", Name)) - if err := c.collectClientShares(ctx, logger, ch); err != nil { - logger.Error("Error in ClientShares", - slog.Any("err", err), - ) - +func (c *Collector) Collect(_ *types.ScrapeContext, _ *slog.Logger, ch chan<- prometheus.Metric) error { + if err := c.collectClientShares(ch); err != nil { return err } return nil } -// Perflib: SMB Client Shares. -type perflibClientShares struct { - Name string - - AvgDataQueueLength float64 `perflib:"Avg. Data Queue Length"` - AvgReadQueueLength float64 `perflib:"Avg. Read Queue Length"` - AvgSecPerRead float64 `perflib:"Avg. sec/Read"` - AvgSecPerWrite float64 `perflib:"Avg. sec/Write"` - AvgSecPerDataRequest float64 `perflib:"Avg. sec/Data Request"` - AvgWriteQueueLength float64 `perflib:"Avg. Write Queue Length"` - CreditStallsPerSec float64 `perflib:"Credit Stalls/sec"` - CurrentDataQueueLength float64 `perflib:"Current Data Queue Length"` - DataBytesPerSec float64 `perflib:"Data Bytes/sec"` - DataRequestsPerSec float64 `perflib:"Data Requests/sec"` - MetadataRequestsPerSec float64 `perflib:"Metadata Requests/sec"` - ReadBytesTransmittedViaSMBDirectPerSec float64 `perflib:"Read Bytes transmitted via SMB Direct/sec"` - ReadBytesPerSec float64 `perflib:"Read Bytes/sec"` - ReadRequestsTransmittedViaSMBDirectPerSec float64 `perflib:"Read Requests transmitted via SMB Direct/sec"` - ReadRequestsPerSec float64 `perflib:"Read Requests/sec"` - TurboIOReadsPerSec float64 `perflib:"Turbo I/O Reads/sec"` - TurboIOWritesPerSec float64 `perflib:"Turbo I/O Writes/sec"` - WriteBytesTransmittedViaSMBDirectPerSec float64 `perflib:"Write Bytes transmitted via SMB Direct/sec"` - WriteBytesPerSec float64 `perflib:"Write Bytes/sec"` - WriteRequestsTransmittedViaSMBDirectPerSec float64 `perflib:"Write Requests transmitted via SMB Direct/sec"` - WriteRequestsPerSec float64 `perflib:"Write Requests/sec"` -} - -func (c *Collector) collectClientShares(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error { - logger = logger.With(slog.String("collector", Name)) - - var data []perflibClientShares - - if err := v1.UnmarshalObject(ctx.PerfObjects["SMB Client Shares"], &data, logger); err != nil { - return err +func (c *Collector) collectClientShares(ch chan<- prometheus.Metric) error { + perfData, err := c.perfDataCollector.Collect() + if err != nil { + return fmt.Errorf("failed to collect SMB Client Shares metrics: %w", err) } - for _, instance := range data { - if instance.Name == "_Total" { - continue - } - - parsed := strings.FieldsFunc(instance.Name, func(r rune) bool { return r == '\\' }) + for name, data := range perfData { + parsed := strings.FieldsFunc(name, func(r rune) bool { return r == '\\' }) serverValue := parsed[0] shareValue := parsed[1] + // Request time spent on queue. Convert from ticks to seconds. ch <- prometheus.MustNewConstMetric( c.requestQueueSecsTotal, prometheus.CounterValue, - instance.AvgDataQueueLength*perftypes.TicksToSecondScaleFactor, + data[AvgDataQueueLength].FirstValue*perftypes.TicksToSecondScaleFactor, serverValue, shareValue, ) @@ -248,28 +240,28 @@ func (c *Collector) collectClientShares(ctx *types.ScrapeContext, logger *slog.L ch <- prometheus.MustNewConstMetric( c.readRequestQueueSecsTotal, prometheus.CounterValue, - instance.AvgReadQueueLength*perftypes.TicksToSecondScaleFactor, + data[AvgReadQueueLength].FirstValue*perftypes.TicksToSecondScaleFactor, serverValue, shareValue, ) ch <- prometheus.MustNewConstMetric( c.readSecsTotal, prometheus.CounterValue, - instance.AvgSecPerRead*perftypes.TicksToSecondScaleFactor, + data[AvgSecPerRead].FirstValue*perftypes.TicksToSecondScaleFactor, serverValue, shareValue, ) ch <- prometheus.MustNewConstMetric( c.writeSecsTotal, prometheus.CounterValue, - instance.AvgSecPerWrite*perftypes.TicksToSecondScaleFactor, + data[AvgSecPerWrite].FirstValue*perftypes.TicksToSecondScaleFactor, serverValue, shareValue, ) ch <- prometheus.MustNewConstMetric( c.requestSecs, prometheus.CounterValue, - instance.AvgSecPerDataRequest*perftypes.TicksToSecondScaleFactor, + data[AvgSecPerDataRequest].FirstValue*perftypes.TicksToSecondScaleFactor, serverValue, shareValue, ) @@ -277,112 +269,112 @@ func (c *Collector) collectClientShares(ctx *types.ScrapeContext, logger *slog.L ch <- prometheus.MustNewConstMetric( c.writeRequestQueueSecsTotal, prometheus.CounterValue, - instance.AvgWriteQueueLength*perftypes.TicksToSecondScaleFactor, + data[AvgWriteQueueLength].FirstValue*perftypes.TicksToSecondScaleFactor, serverValue, shareValue, ) ch <- prometheus.MustNewConstMetric( c.creditStallsTotal, prometheus.CounterValue, - instance.CreditStallsPerSec, + data[CreditStallsPerSec].FirstValue, serverValue, shareValue, ) ch <- prometheus.MustNewConstMetric( c.currentDataQueued, prometheus.GaugeValue, - instance.CurrentDataQueueLength, + data[CurrentDataQueueLength].FirstValue, serverValue, shareValue, ) ch <- prometheus.MustNewConstMetric( c.dataBytesTotal, prometheus.CounterValue, - instance.DataBytesPerSec, + data[DataBytesPerSec].FirstValue, serverValue, shareValue, ) ch <- prometheus.MustNewConstMetric( c.dataRequestsTotal, prometheus.CounterValue, - instance.DataRequestsPerSec, + data[DataRequestsPerSec].FirstValue, serverValue, shareValue, ) ch <- prometheus.MustNewConstMetric( c.metadataRequestsTotal, prometheus.CounterValue, - instance.MetadataRequestsPerSec, + data[MetadataRequestsPerSec].FirstValue, serverValue, shareValue, ) ch <- prometheus.MustNewConstMetric( c.readBytesTransmittedViaSMBDirectTotal, prometheus.CounterValue, - instance.ReadBytesTransmittedViaSMBDirectPerSec, + data[ReadBytesTransmittedViaSMBDirectPerSec].FirstValue, serverValue, shareValue, ) ch <- prometheus.MustNewConstMetric( c.readBytesTotal, prometheus.CounterValue, - instance.ReadBytesPerSec, + data[ReadBytesPerSec].FirstValue, serverValue, shareValue, ) ch <- prometheus.MustNewConstMetric( c.readRequestsTransmittedViaSMBDirectTotal, prometheus.CounterValue, - instance.ReadRequestsTransmittedViaSMBDirectPerSec, + data[ReadRequestsTransmittedViaSMBDirectPerSec].FirstValue, serverValue, shareValue, ) ch <- prometheus.MustNewConstMetric( c.readsTotal, prometheus.CounterValue, - instance.ReadRequestsPerSec, + data[ReadRequestsPerSec].FirstValue, serverValue, shareValue, ) ch <- prometheus.MustNewConstMetric( c.turboIOReadsTotal, prometheus.CounterValue, - instance.TurboIOReadsPerSec, + data[TurboIOReadsPerSec].FirstValue, serverValue, shareValue, ) ch <- prometheus.MustNewConstMetric( c.TurboIOWritesTotal, prometheus.CounterValue, - instance.TurboIOWritesPerSec, + data[TurboIOWritesPerSec].FirstValue, serverValue, shareValue, ) ch <- prometheus.MustNewConstMetric( c.writeBytesTransmittedViaSMBDirectTotal, prometheus.CounterValue, - instance.WriteBytesTransmittedViaSMBDirectPerSec, + data[WriteBytesTransmittedViaSMBDirectPerSec].FirstValue, serverValue, shareValue, ) ch <- prometheus.MustNewConstMetric( c.writeBytesTotal, prometheus.CounterValue, - instance.WriteBytesPerSec, + data[WriteBytesPerSec].FirstValue, serverValue, shareValue, ) ch <- prometheus.MustNewConstMetric( c.writeRequestsTransmittedViaSMBDirectTotal, prometheus.CounterValue, - instance.WriteRequestsTransmittedViaSMBDirectPerSec, + data[WriteRequestsTransmittedViaSMBDirectPerSec].FirstValue, serverValue, shareValue, ) ch <- prometheus.MustNewConstMetric( c.writesTotal, prometheus.CounterValue, - instance.WriteRequestsPerSec, + data[WriteRequestsPerSec].FirstValue, serverValue, shareValue, ) }