github.com/blixtra/rkt@v0.8.1-0.20160204105720-ab0d1add1a43/pkg/log/log.go (about) 1 // Copyright 2016 The rkt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package log 16 17 import ( 18 "bytes" 19 "fmt" 20 "io" 21 "log" 22 "os" 23 "strings" 24 25 "github.com/hashicorp/errwrap" 26 ) 27 28 // Logger is an extended version of the golang Logger to support structured errors. 29 type Logger struct { 30 debug bool 31 *log.Logger 32 } 33 34 // New creates a new Logger with no Log flags set. 35 func New(out io.Writer, prefix string, debug bool) *Logger { 36 l := &Logger{ 37 debug: debug, 38 Logger: log.New(out, prefix, 0), 39 } 40 l.SetFlags(0) 41 return l 42 } 43 44 // NewLogSet returns a set of Loggers for commonly used output streams: errors, 45 // diagnostics, stdout. The error and stdout streams should generally never be 46 // suppressed. diagnostic can be suppressed by setting the output to 47 // ioutil.Discard. If an output destination is not needed, one can simply 48 // discard it by assigning it to '_'. 49 func NewLogSet(prefix string, debug bool) (stderr, diagnostic, stdout *Logger) { 50 stderr = New(os.Stderr, prefix, debug) 51 diagnostic = New(os.Stderr, prefix, debug) 52 // Debug not used for stdout. 53 stdout = New(os.Stdout, prefix, false) 54 55 return stderr, diagnostic, stdout 56 } 57 58 // SetDebug sets the debug flag to the value of b 59 func (l *Logger) SetDebug(b bool) { l.debug = b } 60 61 // SetOutput sets the output destination for Logger. 62 // FIXME: We can get rid of this once rkt drops support for 1.4. 63 func (l *Logger) SetOutput(w io.Writer) { 64 // No SetOutput exposed in go 1.4. 65 *l = *New(w, l.Prefix(), l.debug) 66 } 67 68 // SetFlags is a wrapper around log.SetFlags that adds and removes, ": " to and 69 // from a prefix. This is needed because ": " is only added by golang's log 70 // package if either of the Lshortfile or Llongfile flags are set. 71 func (l *Logger) SetFlags(flag int) { 72 l.Logger.SetFlags(flag) 73 74 // Only proceed if we've actually got a prefix 75 if l.Prefix() == "" { 76 return 77 } 78 79 const clnSpc = ": " 80 if flag&(log.Lshortfile|log.Llongfile) != 0 { 81 l.SetPrefix(strings.TrimSuffix(l.Prefix(), clnSpc)) 82 } else { 83 l.SetPrefix(l.Prefix() + clnSpc) 84 } 85 } 86 87 func (l *Logger) formatErr(e error, msg string) string { 88 // Get a list of accumulated errors 89 var errors []error 90 errwrap.Walk(e, func(err error) { 91 errors = append(errors, err) 92 }) 93 94 var buf bytes.Buffer 95 buf.WriteString(msg) 96 97 if !l.debug { 98 if len(msg) > 0 { 99 buf.WriteString(": ") 100 } 101 buf.WriteString(errors[len(errors)-1].Error()) 102 } else { 103 for i, childErr := range errors { 104 buf.WriteString(fmt.Sprintf("\n%s%s%v", strings.Repeat(" ", i+1), "└─", childErr)) 105 } 106 } 107 108 return strings.TrimSuffix(buf.String(), "\n") 109 } 110 111 // PrintE prints the msg and its error message(s). 112 func (l *Logger) PrintE(msg string, e error) { 113 l.Print(l.formatErr(e, msg)) 114 } 115 116 // Error is a convenience function for printing errors without a message. 117 func (l *Logger) Error(e error) { 118 l.Print(l.formatErr(e, "")) 119 } 120 121 // FatalE prints a string and error then calls os.Exit(1). 122 func (l *Logger) FatalE(msg string, e error) { 123 l.Fatal(l.formatErr(e, msg)) 124 } 125 126 // PanicE prints a string and error then calls panic. 127 func (l *Logger) PanicE(msg string, e error) { 128 l.Panic(l.formatErr(e, msg)) 129 }