filetime: add collector (#1639)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
This commit is contained in:
parent
d43f6ffdec
commit
f442d6e22a
|
@ -21,6 +21,7 @@ Name | Description | Enabled by default
|
||||||
[dhcp](docs/collector.dhcp.md) | DHCP Server |
|
[dhcp](docs/collector.dhcp.md) | DHCP Server |
|
||||||
[dns](docs/collector.dns.md) | DNS Server |
|
[dns](docs/collector.dns.md) | DNS Server |
|
||||||
[exchange](docs/collector.exchange.md) | Exchange metrics |
|
[exchange](docs/collector.exchange.md) | Exchange metrics |
|
||||||
|
[filetime](docs/collector.filetime.md) | FileTime metrics |
|
||||||
[fsrmquota](docs/collector.fsrmquota.md) | Microsoft File Server Resource Manager (FSRM) Quotas collector |
|
[fsrmquota](docs/collector.fsrmquota.md) | Microsoft File Server Resource Manager (FSRM) Quotas collector |
|
||||||
[hyperv](docs/collector.hyperv.md) | Hyper-V hosts |
|
[hyperv](docs/collector.hyperv.md) | Hyper-V hosts |
|
||||||
[iis](docs/collector.iis.md) | IIS sites and applications |
|
[iis](docs/collector.iis.md) | IIS sites and applications |
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
# filetime collector
|
||||||
|
|
||||||
|
The filetime collector exposes modified timestamps of files in the filesystem.
|
||||||
|
|
||||||
|
The collector
|
||||||
|
|
||||||
|
|||
|
||||||
|
-|-
|
||||||
|
Metric name prefix | `filetime`
|
||||||
|
Enabled by default? | No
|
||||||
|
|
||||||
|
## Flags
|
||||||
|
|
||||||
|
### `--collectors.filetime.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_filetime_mtime_timestamp_seconds` | File modification time | gauge | `file`
|
||||||
|
|
||||||
|
### Example metric
|
||||||
|
|
||||||
|
```
|
||||||
|
# HELP windows_filetime_mtime_timestamp_seconds File modification time
|
||||||
|
# TYPE windows_filetime_mtime_timestamp_seconds gauge
|
||||||
|
windows_filetime_mtime_timestamp_seconds{file="C:\\Users\\admin\\Desktop\\Dashboard.lnk"} 1.726434517e+09
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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!_
|
1
go.mod
1
go.mod
|
@ -5,6 +5,7 @@ go 1.23
|
||||||
require (
|
require (
|
||||||
github.com/Microsoft/hcsshim v0.12.6
|
github.com/Microsoft/hcsshim v0.12.6
|
||||||
github.com/alecthomas/kingpin/v2 v2.4.0
|
github.com/alecthomas/kingpin/v2 v2.4.0
|
||||||
|
github.com/bmatcuk/doublestar/v4 v4.6.1
|
||||||
github.com/dimchansky/utfbom v1.1.1
|
github.com/dimchansky/utfbom v1.1.1
|
||||||
github.com/go-ole/go-ole v1.3.0
|
github.com/go-ole/go-ole v1.3.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -10,6 +10,8 @@ github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 h1:t3eaIm0rUkzbrI
|
||||||
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs=
|
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
|
||||||
|
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector/diskdrive"
|
"github.com/prometheus-community/windows_exporter/pkg/collector/diskdrive"
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector/dns"
|
"github.com/prometheus-community/windows_exporter/pkg/collector/dns"
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector/exchange"
|
"github.com/prometheus-community/windows_exporter/pkg/collector/exchange"
|
||||||
|
"github.com/prometheus-community/windows_exporter/pkg/collector/filetime"
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector/fsrmquota"
|
"github.com/prometheus-community/windows_exporter/pkg/collector/fsrmquota"
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector/hyperv"
|
"github.com/prometheus-community/windows_exporter/pkg/collector/hyperv"
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector/iis"
|
"github.com/prometheus-community/windows_exporter/pkg/collector/iis"
|
||||||
|
@ -90,6 +91,7 @@ func NewWithConfig(config Config) *MetricCollectors {
|
||||||
collectors[diskdrive.Name] = diskdrive.New(&config.DiskDrive)
|
collectors[diskdrive.Name] = diskdrive.New(&config.DiskDrive)
|
||||||
collectors[dns.Name] = dns.New(&config.DNS)
|
collectors[dns.Name] = dns.New(&config.DNS)
|
||||||
collectors[exchange.Name] = exchange.New(&config.Exchange)
|
collectors[exchange.Name] = exchange.New(&config.Exchange)
|
||||||
|
collectors[filetime.Name] = filetime.New(&config.Filetime)
|
||||||
collectors[fsrmquota.Name] = fsrmquota.New(&config.Fsrmquota)
|
collectors[fsrmquota.Name] = fsrmquota.New(&config.Fsrmquota)
|
||||||
collectors[hyperv.Name] = hyperv.New(&config.Hyperv)
|
collectors[hyperv.Name] = hyperv.New(&config.Hyperv)
|
||||||
collectors[iis.Name] = iis.New(&config.IIS)
|
collectors[iis.Name] = iis.New(&config.IIS)
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector/diskdrive"
|
"github.com/prometheus-community/windows_exporter/pkg/collector/diskdrive"
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector/dns"
|
"github.com/prometheus-community/windows_exporter/pkg/collector/dns"
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector/exchange"
|
"github.com/prometheus-community/windows_exporter/pkg/collector/exchange"
|
||||||
|
"github.com/prometheus-community/windows_exporter/pkg/collector/filetime"
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector/fsrmquota"
|
"github.com/prometheus-community/windows_exporter/pkg/collector/fsrmquota"
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector/hyperv"
|
"github.com/prometheus-community/windows_exporter/pkg/collector/hyperv"
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector/iis"
|
"github.com/prometheus-community/windows_exporter/pkg/collector/iis"
|
||||||
|
@ -63,6 +64,7 @@ type Config struct {
|
||||||
DiskDrive diskdrive.Config `yaml:"diskdrive"` //nolint:tagliatelle
|
DiskDrive diskdrive.Config `yaml:"diskdrive"` //nolint:tagliatelle
|
||||||
DNS dns.Config `yaml:"dns"`
|
DNS dns.Config `yaml:"dns"`
|
||||||
Exchange exchange.Config `yaml:"exchange"`
|
Exchange exchange.Config `yaml:"exchange"`
|
||||||
|
Filetime filetime.Config `yaml:"filetime"`
|
||||||
Fsrmquota fsrmquota.Config `yaml:"fsrmquota"`
|
Fsrmquota fsrmquota.Config `yaml:"fsrmquota"`
|
||||||
Hyperv hyperv.Config `yaml:"hyperv"`
|
Hyperv hyperv.Config `yaml:"hyperv"`
|
||||||
IIS iis.Config `yaml:"iis"`
|
IIS iis.Config `yaml:"iis"`
|
||||||
|
@ -115,6 +117,7 @@ var ConfigDefaults = Config{
|
||||||
DiskDrive: diskdrive.ConfigDefaults,
|
DiskDrive: diskdrive.ConfigDefaults,
|
||||||
DNS: dns.ConfigDefaults,
|
DNS: dns.ConfigDefaults,
|
||||||
Exchange: exchange.ConfigDefaults,
|
Exchange: exchange.ConfigDefaults,
|
||||||
|
Filetime: filetime.ConfigDefaults,
|
||||||
Fsrmquota: fsrmquota.ConfigDefaults,
|
Fsrmquota: fsrmquota.ConfigDefaults,
|
||||||
Hyperv: hyperv.ConfigDefaults,
|
Hyperv: hyperv.ConfigDefaults,
|
||||||
IIS: iis.ConfigDefaults,
|
IIS: iis.ConfigDefaults,
|
||||||
|
|
|
@ -0,0 +1,175 @@
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package filetime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/alecthomas/kingpin/v2"
|
||||||
|
"github.com/bmatcuk/doublestar/v4"
|
||||||
|
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/yusufpapurcu/wmi"
|
||||||
|
)
|
||||||
|
|
||||||
|
const Name = "filetime"
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
filePatterns []string
|
||||||
|
}
|
||||||
|
|
||||||
|
var ConfigDefaults = Config{
|
||||||
|
filePatterns: []string{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Collector is a Prometheus Collector for collecting file times.
|
||||||
|
type Collector struct {
|
||||||
|
config Config
|
||||||
|
|
||||||
|
fileMTime *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)
|
||||||
|
|
||||||
|
var filePatterns string
|
||||||
|
|
||||||
|
app.Flag(
|
||||||
|
"collectors.filetime.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, ",")).StringVar(&filePatterns)
|
||||||
|
|
||||||
|
app.Action(func(*kingpin.ParseContext) error {
|
||||||
|
// doublestar.Glob() requires forward slashes
|
||||||
|
c.config.filePatterns = strings.Split(filepath.ToSlash(filePatterns), ",")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Collector) GetName() string {
|
||||||
|
return Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Collector) Close(_ *slog.Logger) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
|
||||||
|
logger.Info("filetime collector is in an experimental state! It may subject to change.",
|
||||||
|
slog.String("collector", Name),
|
||||||
|
)
|
||||||
|
|
||||||
|
c.fileMTime = prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(types.Namespace, Name, "mtime_timestamp_seconds"),
|
||||||
|
"File modification time",
|
||||||
|
[]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(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||||
|
logger = logger.With(slog.String("collector", Name))
|
||||||
|
|
||||||
|
return c.collectGlob(logger, ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// collectWin32 collects file times for each file path in the config. It using Win32 FindFirstFile and FindNextFile.
|
||||||
|
func (c *Collector) collectGlob(logger *slog.Logger, 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(logger, ch, filePattern); err != nil {
|
||||||
|
logger.Error("failed collecting metrics for filepath",
|
||||||
|
slog.String("filepath", filePattern),
|
||||||
|
slog.Any("err", err),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}(filePattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Collector) collectGlobFilePath(logger *slog.Logger, ch chan<- prometheus.Metric, filePattern string) error {
|
||||||
|
basePath, pattern := doublestar.SplitPattern(filePattern)
|
||||||
|
basePathFS := os.DirFS(basePath)
|
||||||
|
|
||||||
|
matches, err := doublestar.Glob(basePathFS, pattern, doublestar.WithFilesOnly())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to glob: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, match := range matches {
|
||||||
|
filePath := filepath.Join(basePath, match)
|
||||||
|
|
||||||
|
fileInfo, err := os.Stat(filePath)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("failed to state file",
|
||||||
|
slog.String("file", filePath),
|
||||||
|
slog.Any("err", err),
|
||||||
|
)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.fileMTime,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
float64(fileInfo.ModTime().UTC().Unix()),
|
||||||
|
filePath,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package filetime_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/prometheus-community/windows_exporter/pkg/collector/filetime"
|
||||||
|
"github.com/prometheus-community/windows_exporter/pkg/testutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkCollector(b *testing.B) {
|
||||||
|
testutils.FuncBenchmarkCollector(b, filetime.Name, filetime.NewWithFlags)
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector/diskdrive"
|
"github.com/prometheus-community/windows_exporter/pkg/collector/diskdrive"
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector/dns"
|
"github.com/prometheus-community/windows_exporter/pkg/collector/dns"
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector/exchange"
|
"github.com/prometheus-community/windows_exporter/pkg/collector/exchange"
|
||||||
|
"github.com/prometheus-community/windows_exporter/pkg/collector/filetime"
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector/fsrmquota"
|
"github.com/prometheus-community/windows_exporter/pkg/collector/fsrmquota"
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector/hyperv"
|
"github.com/prometheus-community/windows_exporter/pkg/collector/hyperv"
|
||||||
"github.com/prometheus-community/windows_exporter/pkg/collector/iis"
|
"github.com/prometheus-community/windows_exporter/pkg/collector/iis"
|
||||||
|
@ -73,6 +74,7 @@ var BuildersWithFlags = map[string]BuilderWithFlags[Collector]{
|
||||||
diskdrive.Name: NewBuilderWithFlags(diskdrive.NewWithFlags),
|
diskdrive.Name: NewBuilderWithFlags(diskdrive.NewWithFlags),
|
||||||
dns.Name: NewBuilderWithFlags(dns.NewWithFlags),
|
dns.Name: NewBuilderWithFlags(dns.NewWithFlags),
|
||||||
exchange.Name: NewBuilderWithFlags(exchange.NewWithFlags),
|
exchange.Name: NewBuilderWithFlags(exchange.NewWithFlags),
|
||||||
|
filetime.Name: NewBuilderWithFlags(filetime.NewWithFlags),
|
||||||
fsrmquota.Name: NewBuilderWithFlags(fsrmquota.NewWithFlags),
|
fsrmquota.Name: NewBuilderWithFlags(fsrmquota.NewWithFlags),
|
||||||
hyperv.Name: NewBuilderWithFlags(hyperv.NewWithFlags),
|
hyperv.Name: NewBuilderWithFlags(hyperv.NewWithFlags),
|
||||||
iis.Name: NewBuilderWithFlags(iis.NewWithFlags),
|
iis.Name: NewBuilderWithFlags(iis.NewWithFlags),
|
||||||
|
|
Loading…
Reference in New Issue