github.com/anchore/syft@v1.38.2/cmd/syft/internal/ui/capture.go (about) 1 package ui 2 3 import ( 4 "io" 5 "os" 6 "time" 7 8 "github.com/anchore/syft/internal/log" 9 ) 10 11 const defaultStdoutLogBufferSize = 1024 12 13 // CaptureStdoutToTraceLog replaces stdout and redirects output to the log as trace lines. The return value is a 14 // function, which is used to stop the current capturing of output and restore the original file. 15 // Example: 16 // 17 // restore := CaptureStdoutToTraceLog() 18 // // here, stdout will be captured and redirected to the provided writer 19 // restore() // block until the output has all been sent to the writer and restore the original stdout 20 func CaptureStdoutToTraceLog() func() { 21 return capture(&os.Stdout, newLogWriter(), defaultStdoutLogBufferSize) 22 } 23 24 func capture(target **os.File, writer io.Writer, bufSize int) func() { 25 original := *target 26 27 r, w, _ := os.Pipe() 28 29 *target = w 30 31 done := make(chan struct{}, 1) 32 33 go func() { 34 defer func() { 35 done <- struct{}{} 36 }() 37 38 buf := make([]byte, bufSize) 39 for original != nil { 40 n, err := r.Read(buf) 41 if n > 0 { 42 _, _ = writer.Write(buf[0:n]) 43 } 44 45 if err != nil { 46 break 47 } 48 } 49 }() 50 51 return func() { 52 if original != nil { 53 _ = w.Close() 54 select { 55 case <-done: 56 case <-time.After(1 * time.Second): 57 log.Debugf("stdout buffer timed out after 1 second") 58 } 59 *target = original 60 original = nil 61 } 62 } 63 }