github.com/mackerelio/mackerel-agent-plugins@v0.89.3/mackerel-plugin-inode/lib/inode.go (about) 1 package mpinode 2 3 import ( 4 "os" 5 "os/exec" 6 "regexp" 7 "runtime" 8 "strconv" 9 "strings" 10 11 mp "github.com/mackerelio/go-mackerel-plugin-helper" 12 "github.com/mackerelio/golib/logging" 13 ) 14 15 var logger = logging.GetLogger("metrics.plugin.inode") 16 17 // InodePlugin plugin 18 type InodePlugin struct{} 19 20 var dfHeaderPattern = regexp.MustCompile( 21 `^Filesystem\s+`, 22 ) 23 24 var dfColumnsPattern = regexp.MustCompile( 25 `^(.+?)\s+(?:(?:\d+)\s+(?:\d+)\s+(?:\d+)\s+(?:\d+%)\s+|(?:\d+)\s+)?(\d+)\s+(\d+)\s+(\d+%)\s+(.+)$`, 26 ) 27 28 var devicePattern = regexp.MustCompile( 29 `^/dev/(.*)$`, 30 ) 31 32 var deviceUnacceptablePattern = regexp.MustCompile( 33 `[^A-Za-z0-9_-]`, 34 ) 35 36 // $ df -iP 37 // Filesystem Inodes IUsed IFree IUse% Mounted on 38 // /dev/xvda1 1310720 131197 1179523 11% / 39 // $ df -i # on Mac OSX (impossible to display only inode information) 40 // Filesystem 512-blocks Used Available Capacity iused ifree %iused Mounted on 41 // /dev/disk1 974737408 176727800 797497608 19% 22154973 99687201 18% / 42 43 // FetchMetrics interface for mackerelplugin 44 func (p InodePlugin) FetchMetrics() (map[string]interface{}, error) { 45 dfOpt := "-i" 46 if runtime.GOOS == "linux" { 47 dfOpt = "-iP" 48 } 49 cmd := exec.Command("df", dfOpt) 50 cmd.Env = append(os.Environ(), "LANG=C") 51 out, err := cmd.Output() 52 if err != nil { 53 logger.Warningf("'df -i' command exited with a non-zero status: '%s'", err) 54 return nil, err 55 } 56 result := make(map[string]interface{}) 57 for _, line := range strings.Split(string(out), "\n") { 58 if dfHeaderPattern.MatchString(line) { 59 continue 60 } else if matches := dfColumnsPattern.FindStringSubmatch(line); matches != nil { 61 name := matches[1] 62 // https://github.com/docker/docker/blob/v1.5.0/daemon/graphdriver/devmapper/deviceset.go#L981 63 if regexp.MustCompile(`^/dev/mapper/docker-`).FindStringSubmatch(name) != nil { 64 continue 65 } 66 if nameMatches := devicePattern.FindStringSubmatch(name); nameMatches != nil { 67 device := deviceUnacceptablePattern.ReplaceAllString(nameMatches[1], "_") 68 iused, err := strconv.ParseInt(matches[2], 0, 64) 69 if err != nil { 70 logger.Warningf("Failed to parse value: [%s]", matches[2]) 71 continue 72 } 73 ifree, err := strconv.ParseInt(matches[3], 0, 64) 74 if err != nil { 75 logger.Warningf("Failed to parse value: [%s]", matches[3]) 76 continue 77 } 78 result["inode.count."+device+".used"] = uint64(iused) 79 result["inode.count."+device+".free"] = uint64(ifree) 80 result["inode.count."+device+".total"] = uint64(iused + ifree) 81 usedPercentage := 100.0 // 100% if both iused and ifree are 0 82 if iused+ifree > 0 { 83 usedPercentage = float64(iused) * 100 / float64(iused+ifree) 84 } 85 result["inode.percentage."+device+".used"] = usedPercentage 86 } 87 } 88 } 89 return result, nil 90 } 91 92 // GraphDefinition interface for mackerelplugin 93 func (p InodePlugin) GraphDefinition() map[string]mp.Graphs { 94 return map[string]mp.Graphs{ 95 "inode.count.#": { 96 Label: "Inode", 97 Unit: "integer", 98 Metrics: []mp.Metrics{ 99 {Name: "used", Label: "used"}, 100 {Name: "free", Label: "free"}, 101 {Name: "total", Label: "total"}, 102 }, 103 }, 104 "inode.percentage.#": { 105 Label: "Inode Percentage", 106 Unit: "percentage", 107 Metrics: []mp.Metrics{ 108 {Name: "used", Label: "used %"}, 109 }, 110 }, 111 } 112 } 113 114 // Do the plugin 115 func Do() { 116 inode := InodePlugin{} 117 helper := mp.NewMackerelPlugin(inode) 118 helper.Run() 119 }