github.com/hashicorp/packer@v1.14.3/log.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package main 5 6 import ( 7 "bufio" 8 "bytes" 9 "fmt" 10 "io" 11 "os" 12 "strings" 13 ) 14 15 // These are the environmental variables that determine if we log, and if 16 // we log whether or not the log should go to a file. 17 const EnvLog = "PACKER_LOG" //Set to True 18 const EnvLogFile = "PACKER_LOG_PATH" //Set to a file 19 20 // logOutput determines where we should send logs (if anywhere). 21 func logOutput() (logOutput io.Writer, err error) { 22 if logPath := os.Getenv(EnvLogFile); logPath != "" { 23 var err error 24 logOutput, err = os.Create(logPath) 25 if err != nil { 26 return nil, err 27 } 28 29 return logOutput, nil 30 } 31 32 // If we don't output logs to a file, or it wasn't requested, we can 33 // return immediately without setting an output for logs. 34 if os.Getenv(EnvLog) == "" || os.Getenv(EnvLog) == "0" { 35 return 36 } 37 38 // no path; do a little light filtering to avoid double-dipping UI 39 // calls. 40 r, w := io.Pipe() 41 scanner := bufio.NewScanner(r) 42 scanner.Split(ScanLinesSmallerThanBuffer) 43 44 go func(scanner *bufio.Scanner) { 45 defer w.Close() 46 47 for scanner.Scan() { 48 if strings.Contains(scanner.Text(), "ui:") { 49 continue 50 } 51 if strings.Contains(scanner.Text(), "ui error:") { 52 continue 53 } 54 fmt.Fprintf(os.Stderr, "%s", scanner.Text()+"\n") 55 } 56 if err := scanner.Err(); err != nil { 57 fmt.Fprintf(os.Stderr, "log output filter failed: %s\n", err) 58 } 59 }(scanner) 60 logOutput = w 61 62 return 63 } 64 65 // The below functions come from bufio.Scanner with a small tweak, to fix an 66 // edgecase where the default ScanFunc fails: sometimes, if someone tries to 67 // log a line that is longer than 64*1024 bytes long before it contains a 68 // newline, the ScanLine will continue to return, requesting more data from the 69 // buffer, which can't increase in size anymore, causing a hang. 70 func dropCR(data []byte) []byte { 71 if len(data) > 0 && data[len(data)-1] == '\r' { 72 return data[0 : len(data)-1] 73 } 74 return data 75 } 76 77 func ScanLinesSmallerThanBuffer(data []byte, atEOF bool) (advance int, token []byte, err error) { 78 if atEOF && len(data) == 0 { 79 return 0, nil, nil 80 } 81 if i := bytes.IndexByte(data, '\n'); i >= 0 { 82 // We have a full newline-terminated line. 83 return i + 1, dropCR(data[0:i]), nil 84 } 85 // If we're at EOF, we have a final, non-terminated line. Return it. 86 if atEOF { 87 return len(data), dropCR(data), nil 88 } 89 90 // Our tweak: 91 // Buffer is full, so we can't get more data. Just return what we have as 92 // its own token so we can keep going, even though there's no newline. 93 if len(data)+1 >= bufio.MaxScanTokenSize { 94 return len(data), data[0 : len(data)-1], nil 95 } 96 97 // Request more data. 98 return 0, nil, nil 99 }