package log import ( "fmt" "io" "os" "runtime" "sync" "github.com/apex/log" acli "github.com/apex/log/handlers/cli" "github.com/davecgh/go-spew/spew" "github.com/pterm/pterm" ) // Level is an alias for apex/log.Level for use by callers without importing apex/log. type Level = log.Level var ( // mu protects the output writers mu sync.RWMutex // stdout is the writer for progress output stdout io.Writer = os.Stdout // stderr is the writer for log output stderr io.Writer = os.Stderr ) // SetOutput configures the output writers for the log package. // stdout is used for progress output, stderr is used for log messages. func SetOutput(out, err io.Writer) { mu.Lock() defer mu.Unlock() stdout = out stderr = err pterm.SetDefaultOutput(out) } // GetStdout returns the configured stdout writer. func GetStdout() io.Writer { mu.RLock() defer mu.RUnlock() return stdout } // GetStderr returns the configured stderr writer. func GetStderr() io.Writer { mu.RLock() defer mu.RUnlock() return stderr } // DisableStyling turns off colors and styling for terminal output. func DisableStyling() { pterm.DisableColor() pterm.DisableStyling() pterm.Debug.Prefix.Text = "" pterm.Info.Prefix.Text = "" pterm.Success.Prefix.Text = "" pterm.Warning.Prefix.Text = "" pterm.Error.Prefix.Text = "" pterm.Fatal.Prefix.Text = "" } // Init initializes the logger with the CLI handler and default log level. func Init() { mu.RLock() w := stderr mu.RUnlock() log.SetHandler(acli.New(w)) log.SetLevel(log.InfoLevel) } // Infof logs a formatted message at info level. func Infof(format string, args ...interface{}) { log.Infof(format, args...) } // Info logs a message at info level. func Info(arg string) { log.Info(arg) } // Debugf logs a formatted message at debug level with caller location. func Debugf(format string, args ...interface{}) { DebugReal(fmt.Sprintf(format, args...), 2) } // Debug logs a message at debug level with caller location. func Debug(arg string) { DebugReal(arg, 2) } // DebugReal logs at debug level with caller info from the specified stack depth. func DebugReal(arg string, cs int) { _, callerFile, callerLine, ok := runtime.Caller(cs) if !ok { return } tag := fmt.Sprintf("%s:%d: ", callerFile, callerLine) log.Debug(tag + arg) } // Dump logs a spew dump of the arguments at debug level. func Dump(args ...interface{}) { DebugReal(spew.Sdump(args...), 2) } // EnableDebugLogging sets the log level to debug. func EnableDebugLogging() { SetLevel(log.DebugLevel) } // VerbosityStepsToLogLevel converts a -v count to a log level. // 0 returns InfoLevel, 1+ returns DebugLevel. func VerbosityStepsToLogLevel(l int) log.Level { switch l { case 0: return log.InfoLevel case 1: return log.DebugLevel } // -vv or more return log.DebugLevel } // SetLevelFromVerbosity sets the log level based on -v flag count. func SetLevelFromVerbosity(l int) { SetLevel(VerbosityStepsToLogLevel(l)) } // SetLevel sets the global log level. func SetLevel(arg log.Level) { log.SetLevel(arg) } // GetLogger returns the underlying apex/log Logger. func GetLogger() *log.Logger { if logger, ok := log.Log.(*log.Logger); ok { return logger } panic("unable to get logger") } // GetLevel returns the current log level. func GetLevel() log.Level { return GetLogger().Level } // WithError returns a log entry with the error attached. func WithError(e error) *log.Entry { return GetLogger().WithError(e) } // Progressf prints a progress message that overwrites the current line. // Use ProgressDone() when progress is complete to move to the next line. func Progressf(format string, args ...interface{}) { pterm.Printf("\r"+format, args...) } // ProgressDone completes a progress line by printing a newline. func ProgressDone() { pterm.Println() }