bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/scollector/collectors/chef_linux.go (about) 1 package collectors 2 3 import ( 4 "bufio" 5 "encoding/json" 6 "fmt" 7 "io" 8 "os" 9 "strings" 10 "time" 11 12 "bosun.org/metadata" 13 "bosun.org/opentsdb" 14 ) 15 16 type crSummary struct { 17 StartTime string `json:"start_time"` 18 EndTime string `json:"end_time"` 19 ElapsedTime float32 `json:"elapsed_time"` 20 Status string `json:"status"` 21 } 22 23 func init() { 24 collectors = append(collectors, &IntervalCollector{F: c_chef_linux, Enable: chefEnable}) 25 } 26 27 const ( 28 chefRunLog = "/var/chef/reports/run_status_json.log" 29 ) 30 31 func chefEnable() bool { 32 _, err := os.Stat(chefRunLog) 33 return err == nil 34 } 35 36 func c_chef_linux() (opentsdb.MultiDataPoint, error) { 37 md, err := chef_linux(chefRunLog) 38 if err != nil { 39 return nil, err 40 } 41 return md, nil 42 } 43 44 func chef_linux(logfile string) (opentsdb.MultiDataPoint, error) { 45 var md opentsdb.MultiDataPoint 46 var cr crSummary 47 // Gather stats from the run summary 48 s, err := readLastLine(chefRunLog, 4096) 49 if err != nil { 50 return nil, err 51 } 52 53 err = json.Unmarshal([]byte(s), &cr) 54 if err != nil { 55 return nil, err 56 } 57 58 endTime, err := time.Parse("2006-01-02 15:04:05 -0700", cr.EndTime) 59 if err != nil { 60 return nil, err 61 } 62 63 switch cr.Status { 64 case "success": 65 AddTS(&md, "chef.run_status", endTime.Unix(), 0, nil, metadata.Gauge, metadata.Bool, descChefRunStatus) 66 case "failed": 67 AddTS(&md, "chef.run_status", endTime.Unix(), 1, nil, metadata.Gauge, metadata.Bool, descChefRunStatus) 68 default: 69 err := fmt.Errorf("bad chef run status: %s", cr.Status) 70 return nil, err 71 } 72 AddTS(&md, "chef.run_time", endTime.Unix(), cr.ElapsedTime, nil, metadata.Gauge, metadata.Second, descChefRunTime) 73 74 lastrun_delay := time.Now().Sub(endTime).Seconds() 75 Add(&md, "chef.lastrun_delay", lastrun_delay, nil, metadata.Gauge, metadata.Second, descChefLastRunDelay) 76 77 return md, nil 78 } 79 80 const ( 81 descChefRunStatus = "Status of Chef run." 82 descChefRunTime = "Time which Chef took to run." 83 descChefLastRunDelay = "Time passed after last Chef run." 84 ) 85 86 func readLastLines(filepath string, count int, offset int64) ([]string, error) { 87 f, err := os.Open(filepath) 88 if err != nil { 89 return nil, err 90 } 91 defer f.Close() 92 93 stat, err := f.Stat() 94 if err != nil { 95 return nil, err 96 } 97 98 size := stat.Size() 99 if size < offset { 100 offset = size 101 } 102 103 buf := make([]byte, offset) 104 if _, err := f.ReadAt(buf, size-offset); err != nil && err != io.EOF { 105 return nil, err 106 } 107 108 scanner := bufio.NewScanner(strings.NewReader(string(buf[:]))) 109 110 scanner.Split(bufio.ScanLines) 111 lines := []string{} 112 for scanner.Scan() { 113 lines = append(lines, scanner.Text()) 114 } 115 116 to := len(lines) 117 from := len(lines) - count 118 119 if from < 0 { 120 from = 0 121 } 122 123 return lines[from:to], nil 124 } 125 126 func readLastLine(filepath string, offset int64) (string, error) { 127 res, err := readLastLines(filepath, 1, offset) 128 129 if err != nil { 130 return "", err 131 } 132 133 if len(res) != 1 { 134 err := fmt.Errorf("wrong slice size") 135 return "", err 136 } 137 return res[0], nil 138 }