Add non-zero exit + error message to soong metrics

Improves some error messages that would have given little/no information

Bug: 254650145
Test: relevant unit tests
Test: Induce errors and check metrics proto
Change-Id: Ife6116af74af6e62c2f8ae8774e53c28178fb8d0
This commit is contained in:
Liz Kammer
2022-10-21 10:42:35 -04:00
parent 706d35fe7c
commit f2a80c6396
10 changed files with 326 additions and 191 deletions

View File

@@ -25,4 +25,7 @@ bootstrap_go_package {
testSrcs: [
"logger_test.go",
],
deps: [
"soong-ui-metrics",
],
}

View File

@@ -29,6 +29,7 @@
package logger
import (
"android/soong/ui/metrics"
"errors"
"fmt"
"io"
@@ -72,8 +73,8 @@ type Logger interface {
Output(calldepth int, str string) error
}
// fatalLog is the type used when Fatal[f|ln]
type fatalLog struct {
// fatalError is the type used when Fatal[f|ln]
type fatalError struct {
error
}
@@ -127,7 +128,7 @@ func Recover(fn func(err error)) {
if p == nil {
return
} else if log, ok := p.(fatalLog); ok {
} else if log, ok := p.(fatalError); ok {
fn(error(log))
} else {
panic(p)
@@ -141,6 +142,7 @@ type stdLogger struct {
fileLogger *log.Logger
mutex sync.Mutex
file *os.File
metrics *metrics.Metrics
}
var _ Logger = &stdLogger{}
@@ -149,9 +151,14 @@ var _ Logger = &stdLogger{}
// os.Stderr, but it may be a buffer for tests, or a separate log file if
// the user doesn't need to see the output.
func New(out io.Writer) *stdLogger {
return NewWithMetrics(out, nil)
}
func NewWithMetrics(out io.Writer, m *metrics.Metrics) *stdLogger {
return &stdLogger{
stderr: log.New(out, "", log.Ltime),
fileLogger: log.New(ioutil.Discard, "", log.Ldate|log.Lmicroseconds|log.Llongfile),
metrics: m,
}
}
@@ -201,7 +208,7 @@ func (s *stdLogger) Cleanup() {
fatal := false
p := recover()
if _, ok := p.(fatalLog); ok {
if _, ok := p.(fatalError); ok {
fatal = true
p = nil
} else if p != nil {
@@ -217,40 +224,56 @@ func (s *stdLogger) Cleanup() {
}
}
type verbosityLevel int
const (
verboseLog verbosityLevel = iota
infoLog
fatalLog
panicLog
)
// Output writes string to both stderr and the file log.
func (s *stdLogger) Output(calldepth int, str string) error {
s.stderr.Output(calldepth+1, str)
return s.output(calldepth, str, infoLog)
}
// output writes string to stderr, the file log, and if fatal or panic, to metrics.
func (s *stdLogger) output(calldepth int, str string, level verbosityLevel) error {
if level != verboseLog || s.verbose {
s.stderr.Output(calldepth+1, str)
}
if level >= fatalLog {
s.metrics.SetFatalOrPanicMessage(str)
}
return s.fileLogger.Output(calldepth+1, str)
}
// VerboseOutput is equivalent to Output, but only goes to the file log
// unless SetVerbose(true) has been called.
func (s *stdLogger) VerboseOutput(calldepth int, str string) error {
if s.verbose {
s.stderr.Output(calldepth+1, str)
}
return s.fileLogger.Output(calldepth+1, str)
return s.output(calldepth, str, verboseLog)
}
// Print prints to both stderr and the file log.
// Arguments are handled in the manner of fmt.Print.
func (s *stdLogger) Print(v ...interface{}) {
output := fmt.Sprint(v...)
s.Output(2, output)
s.output(2, output, infoLog)
}
// Printf prints to both stderr and the file log.
// Arguments are handled in the manner of fmt.Printf.
func (s *stdLogger) Printf(format string, v ...interface{}) {
output := fmt.Sprintf(format, v...)
s.Output(2, output)
s.output(2, output, infoLog)
}
// Println prints to both stderr and the file log.
// Arguments are handled in the manner of fmt.Println.
func (s *stdLogger) Println(v ...interface{}) {
output := fmt.Sprintln(v...)
s.Output(2, output)
s.output(2, output, infoLog)
}
// Verbose is equivalent to Print, but only goes to the file log unless
@@ -278,43 +301,43 @@ func (s *stdLogger) Verboseln(v ...interface{}) {
// Cleanup will convert to a os.Exit(1).
func (s *stdLogger) Fatal(v ...interface{}) {
output := fmt.Sprint(v...)
s.Output(2, output)
panic(fatalLog{errors.New(output)})
s.output(2, output, fatalLog)
panic(fatalError{errors.New(output)})
}
// Fatalf is equivalent to Printf() followed by a call to panic() that
// Cleanup will convert to a os.Exit(1).
func (s *stdLogger) Fatalf(format string, v ...interface{}) {
output := fmt.Sprintf(format, v...)
s.Output(2, output)
panic(fatalLog{errors.New(output)})
s.output(2, output, fatalLog)
panic(fatalError{errors.New(output)})
}
// Fatalln is equivalent to Println() followed by a call to panic() that
// Cleanup will convert to a os.Exit(1).
func (s *stdLogger) Fatalln(v ...interface{}) {
output := fmt.Sprintln(v...)
s.Output(2, output)
panic(fatalLog{errors.New(output)})
s.output(2, output, fatalLog)
panic(fatalError{errors.New(output)})
}
// Panic is equivalent to Print() followed by a call to panic().
func (s *stdLogger) Panic(v ...interface{}) {
output := fmt.Sprint(v...)
s.Output(2, output)
s.output(2, output, panicLog)
panic(output)
}
// Panicf is equivalent to Printf() followed by a call to panic().
func (s *stdLogger) Panicf(format string, v ...interface{}) {
output := fmt.Sprintf(format, v...)
s.Output(2, output)
s.output(2, output, panicLog)
panic(output)
}
// Panicln is equivalent to Println() followed by a call to panic().
func (s *stdLogger) Panicln(v ...interface{}) {
output := fmt.Sprintln(v...)
s.Output(2, output)
s.output(2, output, panicLog)
panic(output)
}