file: add file collector to scrape file size and file modify time which can replace filetime collector (#2205)

Co-authored-by: Jan-Otto Kröpke <mail@jkroepke.de>
This commit is contained in:
xieshujian
2025-11-03 01:27:34 +08:00
committed by GitHub
parent ed11d8e8fa
commit 3a9e227bd9
6 changed files with 269 additions and 0 deletions

40
docs/collector.file.md Normal file
View File

@@ -0,0 +1,40 @@
# file collector
The file collector exposes modified timestamps and file size of files in the filesystem.
The collector
|||
-|-
Metric name prefix | `file`
Enabled by default? | No
## Flags
### `--collector.file.file-patterns`
Comma-separated list of file patterns. Each pattern is a glob pattern that can contain `*`, `?`, and `**` (recursive).
See https://github.com/bmatcuk/doublestar#patterns for an extended description of the pattern syntax.
## Metrics
| Name | Description | Type | Labels |
|----------------------------------------|------------------------|-------|--------|
| `windows_file_mtime_timestamp_seconds` | File modification time | gauge | `file` |
| `windows_file_size_bytes` | File size | gauge | `file` |
### Example metric
```
# HELP windows_file_mtime_timestamp_seconds File modification time
# TYPE windows_file_mtime_timestamp_seconds gauge
windows_file_mtime_timestamp_seconds{file="C:\\Users\\admin\\Desktop\\Dashboard.lnk"} 1.726434517e+09
# HELP windows_file_size_bytes File size
# TYPE windows_file_size_bytes gauge
windows_file_size_bytes{file="C:\\Users\\admin\\Desktop\\Dashboard.lnk"} 123
```
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -0,0 +1,188 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build windows
package file
import (
"fmt"
"io/fs"
"log/slog"
"os"
"path/filepath"
"strings"
"sync"
"github.com/alecthomas/kingpin/v2"
"github.com/bmatcuk/doublestar/v4"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
const Name = "file"
type Config struct {
FilePatterns []string `yaml:"file-patterns"`
}
//nolint:gochecknoglobals
var ConfigDefaults = Config{
FilePatterns: []string{},
}
// A Collector is a Prometheus Collector for collecting file times.
type Collector struct {
config Config
logger *slog.Logger
fileMTime *prometheus.Desc
fileSize *prometheus.Desc
}
func New(config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
if config.FilePatterns == nil {
config.FilePatterns = ConfigDefaults.FilePatterns
}
c := &Collector{
config: *config,
}
return c
}
func NewWithFlags(app *kingpin.Application) *Collector {
c := &Collector{
config: ConfigDefaults,
}
c.config.FilePatterns = make([]string, 0)
app.Flag(
"collector.file.file-patterns",
"Comma-separated list of file patterns. Each pattern is a glob pattern that can contain `*`, `?`, and `**` (recursive). See https://github.com/bmatcuk/doublestar#patterns",
).Default(strings.Join(ConfigDefaults.FilePatterns, ",")).StringsVar(&c.config.FilePatterns)
return c
}
func (c *Collector) GetName() string {
return Name
}
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
c.logger = logger.With(slog.String("collector", Name))
c.logger.Info("file collector is in an experimental state! It may subject to change.")
c.fileMTime = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mtime_timestamp_seconds"),
"File modification time",
[]string{"file"},
nil,
)
c.fileSize = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "size_bytes"),
"File size",
[]string{"file"},
nil,
)
for _, filePattern := range c.config.FilePatterns {
basePath, pattern := doublestar.SplitPattern(filePattern)
_, err := doublestar.Glob(os.DirFS(basePath), pattern, doublestar.WithFilesOnly())
if err != nil {
return fmt.Errorf("invalid glob pattern: %w", err)
}
}
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
wg := sync.WaitGroup{}
for _, filePattern := range c.config.FilePatterns {
wg.Add(1)
go func(filePattern string) {
defer wg.Done()
if err := c.collectGlobFilePath(ch, filePattern); err != nil {
c.logger.Error("failed collecting metrics for filepath",
slog.String("filepath", filePattern),
slog.Any("err", err),
)
}
}(filePattern)
}
wg.Wait()
return nil
}
func (c *Collector) collectGlobFilePath(ch chan<- prometheus.Metric, filePattern string) error {
basePath, pattern := doublestar.SplitPattern(filepath.ToSlash(filePattern))
basePathFS := os.DirFS(basePath)
err := doublestar.GlobWalk(basePathFS, pattern, func(path string, d fs.DirEntry) error {
filePath := filepath.Join(basePath, path)
fileInfo, err := os.Stat(filePath)
if err != nil {
c.logger.Warn("failed to state file",
slog.String("file", filePath),
slog.Any("err", err),
)
return nil
}
ch <- prometheus.MustNewConstMetric(
c.fileMTime,
prometheus.GaugeValue,
float64(fileInfo.ModTime().UTC().UnixMicro())/1e6,
filePath,
)
ch <- prometheus.MustNewConstMetric(
c.fileSize,
prometheus.GaugeValue,
float64(fileInfo.Size()),
filePath,
)
return nil
}, doublestar.WithFilesOnly(), doublestar.WithCaseInsensitive())
if err != nil {
return fmt.Errorf("failed to glob: %w", err)
}
return nil
}

View File

@@ -0,0 +1,35 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build windows
package file_test
import (
"testing"
"github.com/prometheus-community/windows_exporter/internal/collector/file"
"github.com/prometheus-community/windows_exporter/internal/utils/testutils"
)
func BenchmarkCollector(b *testing.B) {
testutils.FuncBenchmarkCollector(b, file.Name, file.NewWithFlags)
}
func TestCollector(t *testing.T) {
testutils.TestCollector(t, file.New, &file.Config{
FilePatterns: []string{"*.*"},
})
}

View File

@@ -40,6 +40,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/collector/diskdrive"
"github.com/prometheus-community/windows_exporter/internal/collector/dns"
"github.com/prometheus-community/windows_exporter/internal/collector/exchange"
"github.com/prometheus-community/windows_exporter/internal/collector/file"
"github.com/prometheus-community/windows_exporter/internal/collector/filetime"
"github.com/prometheus-community/windows_exporter/internal/collector/fsrmquota"
"github.com/prometheus-community/windows_exporter/internal/collector/gpu"
@@ -111,6 +112,7 @@ func NewWithConfig(config Config) *Collection {
collectors[dns.Name] = dns.New(&config.DNS)
collectors[exchange.Name] = exchange.New(&config.Exchange)
collectors[filetime.Name] = filetime.New(&config.Filetime)
collectors[file.Name] = file.New(&config.File)
collectors[fsrmquota.Name] = fsrmquota.New(&config.Fsrmquota)
collectors[gpu.Name] = gpu.New(&config.GPU)
collectors[hyperv.Name] = hyperv.New(&config.HyperV)

View File

@@ -30,6 +30,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/collector/diskdrive"
"github.com/prometheus-community/windows_exporter/internal/collector/dns"
"github.com/prometheus-community/windows_exporter/internal/collector/exchange"
"github.com/prometheus-community/windows_exporter/internal/collector/file"
"github.com/prometheus-community/windows_exporter/internal/collector/filetime"
"github.com/prometheus-community/windows_exporter/internal/collector/fsrmquota"
"github.com/prometheus-community/windows_exporter/internal/collector/gpu"
@@ -81,6 +82,7 @@ type Config struct {
DNS dns.Config `yaml:"dns"`
Exchange exchange.Config `yaml:"exchange"`
Filetime filetime.Config `yaml:"filetime"`
File file.Config `yaml:"file"`
Fsrmquota fsrmquota.Config `yaml:"fsrmquota"`
GPU gpu.Config `yaml:"gpu"`
HyperV hyperv.Config `yaml:"hyperv"`

View File

@@ -34,6 +34,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/collector/diskdrive"
"github.com/prometheus-community/windows_exporter/internal/collector/dns"
"github.com/prometheus-community/windows_exporter/internal/collector/exchange"
"github.com/prometheus-community/windows_exporter/internal/collector/file"
"github.com/prometheus-community/windows_exporter/internal/collector/filetime"
"github.com/prometheus-community/windows_exporter/internal/collector/fsrmquota"
"github.com/prometheus-community/windows_exporter/internal/collector/gpu"
@@ -92,6 +93,7 @@ var BuildersWithFlags = map[string]BuilderWithFlags[Collector]{
dns.Name: NewBuilderWithFlags(dns.NewWithFlags),
exchange.Name: NewBuilderWithFlags(exchange.NewWithFlags),
filetime.Name: NewBuilderWithFlags(filetime.NewWithFlags),
file.Name: NewBuilderWithFlags(file.NewWithFlags),
fsrmquota.Name: NewBuilderWithFlags(fsrmquota.NewWithFlags),
gpu.Name: NewBuilderWithFlags(gpu.NewWithFlags),
hyperv.Name: NewBuilderWithFlags(hyperv.NewWithFlags),