github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/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() (close func()) { 21 return capture(&os.Stdout, newLogWriter(), defaultStdoutLogBufferSize) 22 } 23 24 func capture(target **os.File, writer io.Writer, bufSize int) (close 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 { 40 if original == nil { 41 break 42 } 43 44 n, err := r.Read(buf) 45 if n > 0 { 46 _, _ = writer.Write(buf[0:n]) 47 } 48 49 if err != nil { 50 break 51 } 52 } 53 }() 54 55 return func() { 56 if original != nil { 57 _ = w.Close() 58 select { 59 case <-done: 60 case <-time.After(1 * time.Second): 61 log.Debugf("stdout buffer timed out after 1 second") 62 } 63 *target = original 64 original = nil 65 } 66 } 67 }