/
filestat.go
141 lines (121 loc) · 3.1 KB
/
filestat.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//go:generate ../../../tools/readme_config_includer/generator
package filestat
import (
"crypto/md5" //nolint:gosec // G501: Blocklisted import crypto/md5: weak cryptographic primitive - md5 hash is what is desired in this case
_ "embed"
"encoding/hex"
"io"
"os"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal/globpath"
"github.com/influxdata/telegraf/plugins/inputs"
)
//go:embed sample.conf
var sampleConfig string
type FileStat struct {
Md5 bool
Files []string
Log telegraf.Logger
// maps full file paths to globmatch obj
globs map[string]*globpath.GlobPath
// files that were missing - we only log the first time it's not found.
missingFiles map[string]bool
// files that had an error in Stat - we only log the first error.
filesWithErrors map[string]bool
}
func NewFileStat() *FileStat {
return &FileStat{
globs: make(map[string]*globpath.GlobPath),
missingFiles: make(map[string]bool),
filesWithErrors: make(map[string]bool),
}
}
func (*FileStat) SampleConfig() string {
return sampleConfig
}
func (f *FileStat) Gather(acc telegraf.Accumulator) error {
var err error
for _, filepath := range f.Files {
// Get the compiled glob object for this filepath
g, ok := f.globs[filepath]
if !ok {
if g, err = globpath.Compile(filepath); err != nil {
acc.AddError(err)
continue
}
f.globs[filepath] = g
}
files := g.Match()
if len(files) == 0 {
acc.AddFields("filestat",
map[string]interface{}{
"exists": int64(0),
},
map[string]string{
"file": filepath,
})
continue
}
for _, fileName := range files {
tags := map[string]string{
"file": fileName,
}
fields := map[string]interface{}{
"exists": int64(1),
}
fileInfo, err := os.Stat(fileName)
if os.IsNotExist(err) {
fields["exists"] = int64(0)
acc.AddFields("filestat", fields, tags)
if !f.missingFiles[fileName] {
f.Log.Warnf("File %q not found", fileName)
f.missingFiles[fileName] = true
}
continue
}
f.missingFiles[fileName] = false
if fileInfo == nil {
if !f.filesWithErrors[fileName] {
f.filesWithErrors[fileName] = true
f.Log.Errorf("Unable to get info for file %q: %v",
fileName, err)
}
} else {
f.filesWithErrors[fileName] = false
fields["size_bytes"] = fileInfo.Size()
fields["modification_time"] = fileInfo.ModTime().UnixNano()
}
if f.Md5 {
md5Hash, err := getMd5(fileName)
if err != nil {
acc.AddError(err)
} else {
fields["md5_sum"] = md5Hash
}
}
acc.AddFields("filestat", fields, tags)
}
}
return nil
}
// Read given file and calculate a md5 hash.
func getMd5(file string) (string, error) {
of, err := os.Open(file)
if err != nil {
return "", err
}
defer of.Close()
//nolint:gosec // G401: Use of weak cryptographic primitive - md5 hash is what is desired in this case
hash := md5.New()
_, err = io.Copy(hash, of)
if err != nil {
// fatal error
return "", err
}
return hex.EncodeToString(hash.Sum(nil)), nil
}
func init() {
inputs.Add("filestat", func() telegraf.Input {
return NewFileStat()
})
}