github.com/netdata/go.d.plugin@v0.58.1/modules/filecheck/collect_dirs.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package filecheck
     4  
     5  import (
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/netdata/go.d.plugin/agent/module"
    13  )
    14  
    15  func (fc *Filecheck) collectDirs(ms map[string]int64) {
    16  	curTime := time.Now()
    17  	if time.Since(fc.lastDiscoveryDirs) >= fc.DiscoveryEvery.Duration {
    18  		fc.lastDiscoveryDirs = curTime
    19  		fc.curDirs = fc.discoveryDirs()
    20  		fc.updateDirsCharts(fc.curDirs)
    21  	}
    22  
    23  	for _, path := range fc.curDirs {
    24  		fc.collectDir(ms, path, curTime)
    25  	}
    26  	ms["num_of_dirs"] = int64(len(fc.curDirs))
    27  }
    28  
    29  func (fc *Filecheck) collectDir(ms map[string]int64, path string, curTime time.Time) {
    30  	info, err := os.Stat(path)
    31  	if err != nil {
    32  		if os.IsNotExist(err) {
    33  			ms[dirDimID(path, "exists")] = 0
    34  		} else {
    35  			ms[dirDimID(path, "exists")] = 1
    36  		}
    37  		fc.Debug(err)
    38  		return
    39  	}
    40  
    41  	if !info.IsDir() {
    42  		return
    43  	}
    44  
    45  	ms[dirDimID(path, "exists")] = 1
    46  	ms[dirDimID(path, "mtime_ago")] = int64(curTime.Sub(info.ModTime()).Seconds())
    47  	if num, err := calcDirNumOfFiles(path); err == nil {
    48  		ms[dirDimID(path, "num_of_files")] = int64(num)
    49  	}
    50  	if fc.Dirs.CollectDirSize {
    51  		if size, err := calcDirSize(path); err == nil {
    52  			ms[dirDimID(path, "size_bytes")] = size
    53  		}
    54  	}
    55  }
    56  
    57  func (fc Filecheck) discoveryDirs() (dirs []string) {
    58  	for _, path := range fc.Dirs.Include {
    59  		if hasMeta(path) {
    60  			continue
    61  		}
    62  		dirs = append(dirs, path)
    63  	}
    64  
    65  	for _, path := range fc.Dirs.Include {
    66  		if !hasMeta(path) {
    67  			continue
    68  		}
    69  		matches, _ := filepath.Glob(path)
    70  		for _, v := range matches {
    71  			fi, err := os.Lstat(v)
    72  			if err == nil && fi.IsDir() {
    73  				dirs = append(dirs, v)
    74  			}
    75  		}
    76  	}
    77  	return removeDuplicates(dirs)
    78  }
    79  
    80  func (fc *Filecheck) updateDirsCharts(dirs []string) {
    81  	set := make(map[string]bool, len(dirs))
    82  	for _, path := range dirs {
    83  		set[path] = true
    84  		if !fc.collectedDirs[path] {
    85  			fc.collectedDirs[path] = true
    86  			fc.addDirToCharts(path)
    87  		}
    88  	}
    89  	for path := range fc.collectedDirs {
    90  		if !set[path] {
    91  			delete(fc.collectedDirs, path)
    92  			fc.removeDirFromCharts(path)
    93  		}
    94  	}
    95  }
    96  
    97  func (fc *Filecheck) addDirToCharts(path string) {
    98  	for _, chart := range *fc.Charts() {
    99  		if !strings.HasPrefix(chart.ID, "dir_") {
   100  			continue
   101  		}
   102  
   103  		var id string
   104  		switch chart.ID {
   105  		case dirExistenceChart.ID:
   106  			id = dirDimID(path, "exists")
   107  		case dirModTimeChart.ID:
   108  			id = dirDimID(path, "mtime_ago")
   109  		case dirNumOfFilesChart.ID:
   110  			id = dirDimID(path, "num_of_files")
   111  		case dirSizeChart.ID:
   112  			id = dirDimID(path, "size_bytes")
   113  		default:
   114  			fc.Warningf("add dimension: couldn't dim id for '%s' chart (dir '%s')", chart.ID, path)
   115  			continue
   116  		}
   117  
   118  		dim := &module.Dim{ID: id, Name: reSpace.ReplaceAllString(path, "_")}
   119  
   120  		if err := chart.AddDim(dim); err != nil {
   121  			fc.Warning(err)
   122  			continue
   123  		}
   124  		chart.MarkNotCreated()
   125  	}
   126  }
   127  
   128  func (fc *Filecheck) removeDirFromCharts(path string) {
   129  	for _, chart := range *fc.Charts() {
   130  		if !strings.HasPrefix(chart.ID, "dir_") {
   131  			continue
   132  		}
   133  
   134  		var id string
   135  		switch chart.ID {
   136  		case dirExistenceChart.ID:
   137  			id = dirDimID(path, "exists")
   138  		case dirModTimeChart.ID:
   139  			id = dirDimID(path, "mtime_ago")
   140  		case dirNumOfFilesChart.ID:
   141  			id = dirDimID(path, "num_of_files")
   142  		case dirSizeChart.ID:
   143  			id = dirDimID(path, "size_bytes")
   144  		default:
   145  			fc.Warningf("remove dimension: couldn't dim id for '%s' chart (dir '%s')", chart.ID, path)
   146  			continue
   147  		}
   148  
   149  		if err := chart.MarkDimRemove(id, true); err != nil {
   150  			fc.Warning(err)
   151  			continue
   152  		}
   153  		chart.MarkNotCreated()
   154  	}
   155  }
   156  
   157  func dirDimID(path, metric string) string {
   158  	return fmt.Sprintf("dir_%s_%s", reSpace.ReplaceAllString(path, "_"), metric)
   159  }
   160  
   161  func calcDirNumOfFiles(dirpath string) (int, error) {
   162  	f, err := os.Open(dirpath)
   163  	if err != nil {
   164  		return 0, err
   165  	}
   166  	defer func() { _ = f.Close() }()
   167  	// TODO: include dirs?
   168  	names, err := f.Readdirnames(-1)
   169  	return len(names), err
   170  }
   171  
   172  func calcDirSize(dirpath string) (int64, error) {
   173  	var size int64
   174  	err := filepath.Walk(dirpath, func(_ string, info os.FileInfo, err error) error {
   175  		if err != nil {
   176  			return err
   177  		}
   178  		if !info.IsDir() {
   179  			size += info.Size()
   180  		}
   181  		return nil
   182  	})
   183  	return size, err
   184  }