github.com/YousefHaggyHeroku/pack@v1.5.5/internal/logging/logging.go (about) 1 // Package logging implements the logger for the pack CLI. 2 package logging 3 4 import ( 5 "fmt" 6 "io" 7 "io/ioutil" 8 "sync" 9 "time" 10 11 "github.com/apex/log" 12 13 "github.com/YousefHaggyHeroku/pack/internal/style" 14 "github.com/YousefHaggyHeroku/pack/logging" 15 ) 16 17 const ( 18 errorLevelText = "ERROR: " 19 warnLevelText = "Warning: " 20 lineFeed = '\n' 21 // log level to use when quiet is true 22 quietLevel = log.WarnLevel 23 // log level to use when debug is true 24 verboseLevel = log.DebugLevel 25 // time format the out logging uses 26 timeFmt = "2006/01/02 15:04:05.000000" 27 ) 28 29 // LogWithWriters is a logger used with the pack CLI, allowing users to print logs for various levels, including Info, Debug and Error 30 type LogWithWriters struct { 31 sync.Mutex 32 log.Logger 33 wantTime bool 34 clock func() time.Time 35 out io.Writer 36 errOut io.Writer 37 } 38 39 // NewLogWithWriters creates a logger to be used with pack CLI. 40 func NewLogWithWriters(stdout, stderr io.Writer, opts ...func(*LogWithWriters)) *LogWithWriters { 41 lw := &LogWithWriters{ 42 Logger: log.Logger{ 43 Level: log.InfoLevel, 44 }, 45 wantTime: false, 46 clock: time.Now, 47 out: stdout, 48 errOut: stderr, 49 } 50 lw.Logger.Handler = lw 51 52 for _, opt := range opts { 53 opt(lw) 54 } 55 56 return lw 57 } 58 59 // WithClock is an option used to initialize a LogWithWriters with a given clock function 60 func WithClock(clock func() time.Time) func(writers *LogWithWriters) { 61 return func(logger *LogWithWriters) { 62 logger.clock = clock 63 } 64 } 65 66 // WithVerbose is an option used to initialize a LogWithWriters with Verbose turned on 67 func WithVerbose() func(writers *LogWithWriters) { 68 return func(logger *LogWithWriters) { 69 logger.Level = log.DebugLevel 70 } 71 } 72 73 // HandleLog handles log events, printing entries appropriately 74 func (lw *LogWithWriters) HandleLog(e *log.Entry) error { 75 lw.Lock() 76 defer lw.Unlock() 77 78 writer := lw.WriterForLevel(logging.Level(e.Level)) 79 _, err := fmt.Fprint(writer, appendMissingLineFeed(fmt.Sprintf("%s%s", formatLevel(e.Level), e.Message))) 80 81 return err 82 } 83 84 // WriterForLevel returns a Writer for the given logging.Level 85 func (lw *LogWithWriters) WriterForLevel(level logging.Level) io.Writer { 86 if lw.Level > log.Level(level) { 87 return ioutil.Discard 88 } 89 90 if level == logging.ErrorLevel { 91 return NewLogWriter(lw.errOut, lw.clock, lw.wantTime) 92 } 93 94 return NewLogWriter(lw.out, lw.clock, lw.wantTime) 95 } 96 97 // Writer returns the base Writer for the LogWithWriters 98 func (lw *LogWithWriters) Writer() io.Writer { 99 return lw.out 100 } 101 102 // WantTime turns timestamps on in log entries 103 func (lw *LogWithWriters) WantTime(f bool) { 104 lw.wantTime = f 105 } 106 107 // WantQuiet reduces the number of logs returned 108 func (lw *LogWithWriters) WantQuiet(f bool) { 109 if f { 110 lw.Level = quietLevel 111 } 112 } 113 114 // WantVerbose increases the number of logs returned 115 func (lw *LogWithWriters) WantVerbose(f bool) { 116 if f { 117 lw.Level = verboseLevel 118 } 119 } 120 121 // IsVerbose returns whether verbose logging is on 122 func (lw *LogWithWriters) IsVerbose() bool { 123 return lw.Level == log.DebugLevel 124 } 125 126 func formatLevel(ll log.Level) string { 127 switch ll { 128 case log.ErrorLevel: 129 return style.Error(errorLevelText) 130 case log.WarnLevel: 131 return style.Warn(warnLevelText) 132 } 133 134 return "" 135 } 136 137 // Preserve behavior of other loggers 138 func appendMissingLineFeed(msg string) string { 139 buff := []byte(msg) 140 if len(buff) == 0 || buff[len(buff)-1] != lineFeed { 141 buff = append(buff, lineFeed) 142 } 143 return string(buff) 144 }