2024-09-24 21:34:39 +00:00
//go:build windows
package filetime
import (
"fmt"
"log/slog"
"os"
"path/filepath"
"strings"
"sync"
"github.com/alecthomas/kingpin/v2"
"github.com/bmatcuk/doublestar/v4"
2024-11-03 16:23:26 +00:00
"github.com/prometheus-community/windows_exporter/internal/mi"
2024-10-03 18:34:45 +00:00
"github.com/prometheus-community/windows_exporter/internal/types"
2024-09-24 21:34:39 +00:00
"github.com/prometheus/client_golang/prometheus"
)
const Name = "filetime"
type Config struct {
2024-10-06 22:15:54 +00:00
FilePatterns [ ] string
2024-09-24 21:34:39 +00:00
}
var ConfigDefaults = Config {
2024-10-06 22:15:54 +00:00
FilePatterns : [ ] string { } ,
2024-09-24 21:34:39 +00:00
}
// A Collector is a Prometheus Collector for collecting file times.
type Collector struct {
config Config
2024-11-17 20:51:12 +00:00
logger * slog . Logger
2024-09-24 21:34:39 +00:00
fileMTime * prometheus . Desc
}
func New ( config * Config ) * Collector {
if config == nil {
config = & ConfigDefaults
}
2024-10-06 22:15:54 +00:00
if config . FilePatterns == nil {
config . FilePatterns = ConfigDefaults . FilePatterns
2024-09-24 21:34:39 +00:00
}
c := & Collector {
config : * config ,
}
return c
}
func NewWithFlags ( app * kingpin . Application ) * Collector {
c := & Collector {
config : ConfigDefaults ,
}
2024-10-06 22:15:54 +00:00
c . config . FilePatterns = make ( [ ] string , 0 )
2024-09-24 21:34:39 +00:00
var filePatterns string
app . Flag (
2024-10-02 11:24:58 +00:00
"collector.filetime.file-patterns" ,
2024-09-24 21:34:39 +00:00
"Comma-separated list of file patterns. Each pattern is a glob pattern that can contain `*`, `?`, and `**` (recursive). See https://github.com/bmatcuk/doublestar#patterns" ,
2024-10-06 22:15:54 +00:00
) . Default ( strings . Join ( ConfigDefaults . FilePatterns , "," ) ) . StringVar ( & filePatterns )
2024-09-24 21:34:39 +00:00
app . Action ( func ( * kingpin . ParseContext ) error {
// doublestar.Glob() requires forward slashes
2024-10-06 22:15:54 +00:00
c . config . FilePatterns = strings . Split ( filepath . ToSlash ( filePatterns ) , "," )
2024-09-24 21:34:39 +00:00
return nil
} )
return c
}
func ( c * Collector ) GetName ( ) string {
return Name
}
2024-11-17 20:51:12 +00:00
func ( c * Collector ) Close ( ) error {
2024-09-24 21:34:39 +00:00
return nil
}
2024-11-03 16:23:26 +00:00
func ( c * Collector ) Build ( logger * slog . Logger , _ * mi . Session ) error {
2024-11-17 20:51:12 +00:00
c . logger = logger . With ( slog . String ( "collector" , Name ) )
c . logger . Info ( "filetime collector is in an experimental state! It may subject to change." )
2024-09-24 21:34:39 +00:00
c . fileMTime = prometheus . NewDesc (
2024-10-03 18:34:45 +00:00
prometheus . BuildFQName ( types . Namespace , Name , "mtime_timestamp_seconds" ) ,
2024-09-24 21:34:39 +00:00
"File modification time" ,
[ ] string { "file" } ,
nil ,
)
2024-10-06 22:15:54 +00:00
for _ , filePattern := range c . config . FilePatterns {
2024-09-24 21:34:39 +00:00
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.
2024-11-17 20:51:12 +00:00
func ( c * Collector ) Collect ( ch chan <- prometheus . Metric ) error {
2024-09-24 21:34:39 +00:00
wg := sync . WaitGroup { }
2024-10-06 22:15:54 +00:00
for _ , filePattern := range c . config . FilePatterns {
2024-09-24 21:34:39 +00:00
wg . Add ( 1 )
go func ( filePattern string ) {
defer wg . Done ( )
2024-11-17 20:51:12 +00:00
if err := c . collectGlobFilePath ( ch , filePattern ) ; err != nil {
c . logger . Error ( "failed collecting metrics for filepath" ,
2024-09-24 21:34:39 +00:00
slog . String ( "filepath" , filePattern ) ,
slog . Any ( "err" , err ) ,
)
}
} ( filePattern )
}
wg . Wait ( )
return nil
}
2024-11-17 20:51:12 +00:00
func ( c * Collector ) collectGlobFilePath ( ch chan <- prometheus . Metric , filePattern string ) error {
2024-09-24 21:34:39 +00:00
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 {
2024-11-17 20:51:12 +00:00
c . logger . Warn ( "failed to state file" ,
2024-09-24 21:34:39 +00:00
slog . String ( "file" , filePath ) ,
slog . Any ( "err" , err ) ,
)
continue
}
ch <- prometheus . MustNewConstMetric (
c . fileMTime ,
prometheus . GaugeValue ,
float64 ( fileInfo . ModTime ( ) . UTC ( ) . Unix ( ) ) ,
filePath ,
)
}
return nil
}