Skip to content

Commit

Permalink
lots of changes.
Browse files Browse the repository at this point in the history
- config of service port
- propper labels
- info if directory is recursively scanned as label
- works better now
- not suitable for large directories, it takes to much time
  • Loading branch information
Oli committed Dec 16, 2018
1 parent f57db03 commit 703f54c
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 31 deletions.
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,22 @@ get simple directory statistics (metrics) for prometheus
the purpose of this exporter is to generate an alert within prometheus / grafana if there are files waiting longer than a certain time.

## exports
- `dirstat_files_count`: number of files in directory
- `dirstat_oldest_file_age`: age of oldest file in seconds
- `dirstat_files_in_dir`: number of files in directory
- `dirstat_oldest_file_time`: timestamp (unix time) of oldest file in dir

## todos
- make sure only files are counted (done)
- implement recursive file walking (done)
- make information gathering concurrent, so more directories can be handled in the same time
- test handling of unc paths in windows (yes, it's targeted for windows.)
- better logging
- better error handling
- test handling of unc paths in windows (yes, it's targeted for windows.)
- *important* stack items correctly
- make information gathering concurrent, so more directories can be handled in the same time

## notes
- *important* stack items correctly (types and help text must only appear once in a metric export / per request)
- note to self: labels must not contain a single backslash... I replaced all backslashes now with forward slashes. -> there must be a better solution
- e.g. add labels to the configuration and give them meaningful names.

## problems
- it can only handle one directory at the moment because it does not stack the output correctly according to prometheus.
- metrics that have the same metric name must be bundled and must not have repeating help and type information.
- large directories might not be handled well
- might use lot of memory, because whole directory is read once (untested)
2 changes: 2 additions & 0 deletions cfg/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import (
)

type Dir struct {
Name string
Path string
Recursive bool
}
type Config struct {
ServicePort string
Directories []Dir
}

Expand Down
11 changes: 9 additions & 2 deletions config.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
serviceport: "9999"
directories:
- path: \
- path: c:\
name: c:\\
recursive: false
- path: \\localhost\c$\tmp
name: \\localhost\c$\tmp
recursive: true
- path: \users\wuo
recursive: false
name: \users\wuo
recursive: true
- path: \tmp
name: \tmp
recursive: true
106 changes: 84 additions & 22 deletions dirstat.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,79 @@ import (
// globals
var config cfg.Config

const (
namespace = "dirstat"
metricFilesInDir = "files_in_dir"
metricOldestFileTime = "oldest_file_time"
)

type metricValue struct {
dir string
name string
value int64
recursive bool
}
type metric struct {
metricName string
metricHelp string
metricType string
metricValues map[string]metricValue
}

var metricRegister map[string]metric

func main() {
config = cfg.GetConfig()

metricRegister = make(map[string]metric)

metricRegister[metricFilesInDir] = metric{
metricName: metricFilesInDir,
metricType: "gauge",
metricHelp: "this counts all the files in a directory",
metricValues: make(map[string]metricValue),
}
metricRegister[metricOldestFileTime] = metric{
metricName: metricOldestFileTime,
metricType: "gauge",
metricHelp: "displays the timestamp in unix time of the oldes file",
metricValues: make(map[string]metricValue),
}

http.HandleFunc("/metrics", handleMetrics)
if err := http.ListenAndServe(":9999", nil); err != nil {
if err := http.ListenAndServe(":"+config.ServicePort, nil); err != nil {
panic(err)
}
}

func handleMetrics(w http.ResponseWriter, r *http.Request) {
for _, dir := range config.Directories {
if dir.Recursive {
w.Write([]byte(getDirMetric("dirstat", "files_count", dir.Path, int64(getFileCountInDirRecursively(dir.Path)))))
w.Write([]byte(getDirMetric("dirstat", "oldest_file_age", dir.Path, int64(getOldestAgeInDirRecursively(dir.Path)))))
metricRegister[metricFilesInDir].metricValues[dir.Path] = metricValue{
value: int64(getFileCountInDirRecursively(dir.Path)),
recursive: dir.Recursive,
name: dir.Name,
}
metricRegister[metricOldestFileTime].metricValues[dir.Path] = metricValue{
value: int64(getOldestAgeInDirRecursively(dir.Path)),
recursive: dir.Recursive,
name: dir.Name,
}
} else {
w.Write([]byte(getDirMetric("dirstat", "files_count", dir.Path, int64(getFileCountInDir(dir.Path)))))
w.Write([]byte(getDirMetric("dirstat", "oldest_file_age", dir.Path, int64(getOldestAgeInDir(dir.Path)))))
metricRegister[metricFilesInDir].metricValues[dir.Path] = metricValue{
value: int64(getFileCountInDir(dir.Path)),
recursive: dir.Recursive,
name: dir.Name,
}
metricRegister[metricOldestFileTime].metricValues[dir.Path] = metricValue{
value: int64(getOldestAgeInDir(dir.Path)),
recursive: dir.Recursive,
name: dir.Name,
}
}

}
for _, value := range metricRegister {
_, _ = w.Write([]byte(sprintDirMetric(value)))
}
}

Expand All @@ -46,35 +100,34 @@ func getModTime(file string) int64 {
}

func getOldestAgeInDirRecursively(dir string) int64 {
var maxAge int64 = 0
var oldestTs int64 = time.Now().Unix()
_ = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
log.Printf("an error occurred! %v\n", err)
}
if !info.IsDir() {
age := time.Now().Unix() - getModTime(path)
if age > maxAge {
maxAge = age
ts := getModTime(path)
if ts > oldestTs {
oldestTs = ts
}
}
return nil
})
return maxAge
return oldestTs
}

func getOldestAgeInDir(dir string) int64 {
var files, _ = ioutil.ReadDir(dir)
var maxAge int64 = 0
var oldestTs int64 = time.Now().Unix()
for _, file := range files {
//fmt.Println(file)
if !file.IsDir() {
age := time.Now().Unix() - getModTime(dir+string(os.PathSeparator)+file.Name())
if age > maxAge {
maxAge = age
ts := getModTime(dir + string(os.PathSeparator) + file.Name())
if ts < oldestTs {
oldestTs = ts
}
}
}
return maxAge
return oldestTs
}

func getFileCountInDirRecursively(dir string) int {
Expand All @@ -93,12 +146,21 @@ func getFileCountInDirRecursively(dir string) int {

func getFileCountInDir(dir string) int {
files, _ := ioutil.ReadDir(dir)
return len(files)
count := 0
for _, f := range files {
if !f.IsDir() {
count++
}
}
return count
}

func getDirMetric(namespace string, metricName string, dir string, value int64) string {
str := fmt.Sprintf("# HELP %s_%s\n", namespace, metricName)
str += fmt.Sprintf("# TYPE %s_%s counter\n", namespace, metricName)
str += fmt.Sprintf("%s_%s{dir=\"%s\"} %d\n", namespace, metricName, dir, value)
func sprintDirMetric(m metric) string {
str := ""
str += fmt.Sprintf("# HELP %s_%s %s\n", namespace, m.metricName, m.metricHelp)
str += fmt.Sprintf("# TYPE %s_%s %s\n", namespace, m.metricName, m.metricType)
for _, v := range m.metricValues {
str += fmt.Sprintf("%s_%s{dir=\"%s\",recursive=\"%t\"} %v\n", namespace, m.metricName, v.name, v.recursive, v.value)
}
return str
}

0 comments on commit 703f54c

Please sign in to comment.