7159: Return benign os.ErrNotExist error from Compare to avoid excessive logs. refs...
[arvados.git] / services / crunchstat / crunchstat.go
index 356eb018f4664980e705020ed965726f03861c56..e14912423db73483ef2623149e23d3ca63b3dabb 100644 (file)
@@ -37,21 +37,30 @@ type Cgroup struct {
 var childLog = log.New(os.Stderr, "", 0)
 var statLog = log.New(os.Stderr, "crunchstat: ", 0)
 
+const (
+       MaxLogLine = 1 << 14 // Child stderr lines >16KiB will be split
+)
+
 func CopyPipeToChildLog(in io.ReadCloser, done chan<- bool) {
-       reader := bufio.NewReader(in)
+       reader := bufio.NewReaderSize(in, MaxLogLine)
+       var prefix string
        for {
-               line, err := reader.ReadBytes('\n')
-               if len(line) > 0 {
-                       if err == nil {
-                               // err == nil IFF line ends in \n
-                               line = line[:len(line)-1]
-                       }
-                       childLog.Println(string(line))
-               }
+               line, isPrefix, err := reader.ReadLine()
                if err == io.EOF {
                        break
                } else if err != nil {
-                       statLog.Fatalln("line buffering error:", err)
+                       statLog.Fatal("error reading child stderr:", err)
+               }
+               var suffix string
+               if isPrefix {
+                       suffix = "[...]"
+               }
+               childLog.Print(prefix, string(line), suffix)
+               // Set up prefix for following line
+               if isPrefix {
+                       prefix = "[...]"
+               } else {
+                       prefix = ""
                }
        }
        done <- true
@@ -61,7 +70,7 @@ func CopyPipeToChildLog(in io.ReadCloser, done chan<- bool) {
 func ReadAllOrWarn(in *os.File) ([]byte, error) {
        content, err := ioutil.ReadAll(in)
        if err != nil {
-               statLog.Printf("read %s: %s\n", in.Name(), err)
+               statLog.Printf("error reading %s: %s\n", in.Name(), err)
        }
        return content, err
 }
@@ -77,11 +86,19 @@ var reportedStatFile = map[string]string{}
 // cgroup root for the given statgroup. (This will avoid falling back
 // to host-level stats during container setup and teardown.)
 func OpenStatFile(cgroup Cgroup, statgroup string, stat string) (*os.File, error) {
-       var paths = []string{
-               fmt.Sprintf("%s/%s/%s/%s/%s", cgroup.root, statgroup, cgroup.parent, cgroup.cid, stat),
-               fmt.Sprintf("%s/%s/%s/%s", cgroup.root, cgroup.parent, cgroup.cid, stat),
-               fmt.Sprintf("%s/%s/%s", cgroup.root, statgroup, stat),
-               fmt.Sprintf("%s/%s", cgroup.root, stat),
+       var paths []string
+       if cgroup.cid != "" {
+               // Collect container's stats
+               paths = []string{
+                       fmt.Sprintf("%s/%s/%s/%s/%s", cgroup.root, statgroup, cgroup.parent, cgroup.cid, stat),
+                       fmt.Sprintf("%s/%s/%s/%s", cgroup.root, cgroup.parent, cgroup.cid, stat),
+               }
+       } else {
+               // Collect this host's stats
+               paths = []string{
+                       fmt.Sprintf("%s/%s/%s", cgroup.root, statgroup, stat),
+                       fmt.Sprintf("%s/%s", cgroup.root, stat),
+               }
        }
        var path string
        var file *os.File
@@ -101,12 +118,14 @@ func OpenStatFile(cgroup Cgroup, statgroup string, stat string) (*os.File, error
                // whether we happen to collect stats [a] before any
                // processes have been created in the container and
                // [b] after all contained processes have exited.
-               reportedStatFile[stat] = path
                if path == "" {
-                       statLog.Printf("did not find stats file: stat %s, statgroup %s, cid %s, parent %s, root %s\n", stat, statgroup, cgroup.cid, cgroup.parent, cgroup.root)
+                       statLog.Printf("notice: stats not available: stat %s, statgroup %s, cid %s, parent %s, root %s\n", stat, statgroup, cgroup.cid, cgroup.parent, cgroup.root)
+               } else if ok {
+                       statLog.Printf("notice: stats moved from %s to %s\n", reportedStatFile[stat], path)
                } else {
-                       statLog.Printf("reading stats from %s\n", path)
+                       statLog.Printf("notice: reading stats from %s\n", path)
                }
+               reportedStatFile[stat] = path
        }
        return file, err
 }
@@ -364,7 +383,7 @@ func run(logger *log.Logger) error {
        flag.Parse()
 
        if cgroup_root == "" {
-               statLog.Fatalln("Must provide -cgroup-root")
+               statLog.Fatal("error: must provide -cgroup-root")
        }
 
        finish_chan := make(chan bool)
@@ -376,7 +395,7 @@ func run(logger *log.Logger) error {
                // Set up subprocess
                cmd = exec.Command(flag.Args()[0], flag.Args()[1:]...)
 
-               statLog.Println("Running ", flag.Args())
+               childLog.Println("Running", flag.Args())
 
                // Child process will use our stdin and stdout pipes
                // (we close our copies below)
@@ -398,13 +417,13 @@ func run(logger *log.Logger) error {
                // Funnel stderr through our channel
                stderr_pipe, err := cmd.StderrPipe()
                if err != nil {
-                       statLog.Fatalln("stderr:", err)
+                       statLog.Fatalln("error in StderrPipe:", err)
                }
                go CopyPipeToChildLog(stderr_pipe, finish_chan)
 
                // Run subprocess
                if err := cmd.Start(); err != nil {
-                       statLog.Fatalln("cmd.Start:", err)
+                       statLog.Fatalln("error in cmd.Start:", err)
                }
 
                // Close stdin/stdout in this (parent) process
@@ -428,7 +447,7 @@ func run(logger *log.Logger) error {
                        time.Sleep(100 * time.Millisecond)
                }
                if !ok {
-                       statLog.Println("Could not read cid file:", cgroup_cidfile)
+                       statLog.Println("error reading cid file:", cgroup_cidfile)
                }
        }
 
@@ -461,7 +480,7 @@ func main() {
                                os.Exit(status.ExitStatus())
                        }
                } else {
-                       statLog.Fatalln("cmd.Wait:", err)
+                       statLog.Fatalln("error in cmd.Wait:", err)
                }
        }
 }