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  }