Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions docs/collector.file.md
Original file line number Diff line number Diff line change
@@ -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!_
188 changes: 188 additions & 0 deletions internal/collector/file/file.go
Original file line number Diff line number Diff line change
@@ -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
}
35 changes: 35 additions & 0 deletions internal/collector/file/file_test.go
Original file line number Diff line number Diff line change
@@ -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{"*.*"},
})
}
2 changes: 2 additions & 0 deletions pkg/collector/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions pkg/collector/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"`
Expand Down
2 changes: 2 additions & 0 deletions pkg/collector/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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),
Expand Down
Loading