telegraf/plugins/inputs/filecount/filecount.go

216 lines
4.2 KiB
Go

package filecount
import (
"os"
"path/filepath"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/plugins/inputs"
)
const sampleConfig = `
## Directory to gather stats about.
directory = "/var/cache/apt/archives"
## Only count files that match the name pattern. Defaults to "*".
name = "*.deb"
## Count files in subdirectories. Defaults to true.
recursive = false
## Only count regular files. Defaults to true.
regular_only = true
## Only count files that are at least this size in bytes. If size is
## a negative number, only count files that are smaller than the
## absolute value of size. Defaults to 0.
size = 0
## Only count files that have not been touched for at least this
## duration. If mtime is negative, only count files that have been
## touched in this duration. Defaults to "0s".
mtime = "0s"
`
type FileCount struct {
Directory string
Name string
Recursive bool
RegularOnly bool
Size int64
MTime internal.Duration `toml:"mtime"`
fileFilters []fileFilterFunc
}
type countFunc func(os.FileInfo)
type fileFilterFunc func(os.FileInfo) (bool, error)
func (_ *FileCount) Description() string {
return "Count files in a directory"
}
func (_ *FileCount) SampleConfig() string { return sampleConfig }
func rejectNilFilters(filters []fileFilterFunc) []fileFilterFunc {
filtered := make([]fileFilterFunc, 0, len(filters))
for _, f := range filters {
if f != nil {
filtered = append(filtered, f)
}
}
return filtered
}
func (fc *FileCount) nameFilter() fileFilterFunc {
if fc.Name == "*" {
return nil
}
return func(f os.FileInfo) (bool, error) {
match, err := filepath.Match(fc.Name, f.Name())
if err != nil {
return false, err
}
return match, nil
}
}
func (fc *FileCount) regularOnlyFilter() fileFilterFunc {
if !fc.RegularOnly {
return nil
}
return func(f os.FileInfo) (bool, error) {
return f.Mode().IsRegular(), nil
}
}
func (fc *FileCount) sizeFilter() fileFilterFunc {
if fc.Size == 0 {
return nil
}
return func(f os.FileInfo) (bool, error) {
if !f.Mode().IsRegular() {
return false, nil
}
if fc.Size < 0 {
return f.Size() < -fc.Size, nil
}
return f.Size() >= fc.Size, nil
}
}
func (fc *FileCount) mtimeFilter() fileFilterFunc {
if fc.MTime.Duration == 0 {
return nil
}
return func(f os.FileInfo) (bool, error) {
age := absDuration(fc.MTime.Duration)
mtime := time.Now().Add(-age)
if fc.MTime.Duration < 0 {
return f.ModTime().After(mtime), nil
}
return f.ModTime().Before(mtime), nil
}
}
func absDuration(x time.Duration) time.Duration {
if x < 0 {
return -x
}
return x
}
func count(basedir string, recursive bool, countFn countFunc) error {
walkFn := func(path string, file os.FileInfo, err error) error {
if path == basedir {
return nil
}
countFn(file)
if !recursive && file.IsDir() {
return filepath.SkipDir
}
return nil
}
return filepath.Walk(basedir, walkFn)
}
func (fc *FileCount) initFileFilters() {
filters := []fileFilterFunc{
fc.nameFilter(),
fc.regularOnlyFilter(),
fc.sizeFilter(),
fc.mtimeFilter(),
}
fc.fileFilters = rejectNilFilters(filters)
}
func (fc *FileCount) filter(file os.FileInfo) (bool, error) {
if fc.fileFilters == nil {
fc.initFileFilters()
}
for _, fileFilter := range fc.fileFilters {
match, err := fileFilter(file)
if err != nil {
return false, err
}
if !match {
return false, nil
}
}
return true, nil
}
func (fc *FileCount) Gather(acc telegraf.Accumulator) error {
numFiles := int64(0)
countFn := func(f os.FileInfo) {
match, err := fc.filter(f)
if err != nil {
acc.AddError(err)
return
}
if !match {
return
}
numFiles++
}
err := count(fc.Directory, fc.Recursive, countFn)
if err != nil {
acc.AddError(err)
}
acc.AddFields("filecount",
map[string]interface{}{
"count": numFiles,
},
map[string]string{
"directory": fc.Directory,
})
return nil
}
func NewFileCount() *FileCount {
return &FileCount{
Directory: "",
Name: "*",
Recursive: true,
RegularOnly: true,
Size: 0,
MTime: internal.Duration{Duration: 0},
fileFilters: nil,
}
}
func init() {
inputs.Add("filecount", func() telegraf.Input {
return NewFileCount()
})
}