code-intelligence.com/cifuzz@v0.40.0/pkg/log/log.go (about)

     1  package log
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  
     8  	"github.com/pkg/errors"
     9  	"github.com/pterm/pterm"
    10  	"github.com/spf13/viper"
    11  	"golang.org/x/term"
    12  )
    13  
    14  var disableColor bool
    15  
    16  // Output is the primary outlet for the log to write to.
    17  var Output io.Writer
    18  
    19  // VerboseSecondaryOutput captures the complete verbose output
    20  // of the primary output, even when the command is not called
    21  // in verbose mode. It provides a secondary output option.
    22  var VerboseSecondaryOutput io.Writer
    23  
    24  func init() {
    25  	Output = os.Stderr
    26  	// Disable color if stderr is not a terminal. We don't use
    27  	// the color flag here because that would disable color for all
    28  	// pterm and color methods, but we might want to use color in output
    29  	// printed to stdout (if stdout is a terminal).
    30  	disableColor = !term.IsTerminal(int(os.Stderr.Fd()))
    31  }
    32  
    33  func log(style pterm.Style, icon string, a ...any) {
    34  	s := fmt.Sprint(a...)
    35  	if len(s) == 0 || s[len(s)-1] != '\n' {
    36  		s += "\n"
    37  	}
    38  
    39  	// Can't do this check in init() because the flag is not yet set at that point
    40  	switch {
    41  	case PlainStyle():
    42  		// Disable all colors (this also influences all other pterm writers)
    43  		// Don't use DisableStyling() because otherwise the spinners won't work anymore
    44  		pterm.DisableColor()
    45  	case viper.GetString("style") == "color":
    46  		s = style.Sprint(s)
    47  	default:
    48  		s = icon + s
    49  		s = style.Sprint(s)
    50  	}
    51  
    52  	if disableColor {
    53  		s = pterm.RemoveColorFromString(s)
    54  	}
    55  
    56  	// Clear the updating printer output if any. We don't use
    57  	// pterm.Fprint here, which also tries to clear spinner printer
    58  	// output, because that only works when the spinner printer and this
    59  	// function write to the same output stream, which is not always the
    60  	// case, because we let the spinner printer write to stdout unless
    61  	// --json was used. The advantage of that is that it allows piping
    62  	// stderr to a log file while still seeing the output that's mostly
    63  	// relevant during execution. But if that continues to add complexity
    64  	// to our code, we might want to reassess the cost/benefit.
    65  	if ActiveUpdatingPrinter != nil {
    66  		ActiveUpdatingPrinter.Clear()
    67  	}
    68  
    69  	// If a progress spinner is currently running, we have to stop it,
    70  	// then print the log and start it again to have a clean output
    71  	// If we don't do this, the spinner will remain on the console
    72  	// between the logs
    73  	if currentProgressSpinner != nil {
    74  		// We only need to set this if we have to restart the spinner
    75  		currentProgressSpinner.RemoveWhenDone = true
    76  		_ = currentProgressSpinner.Stop()
    77  
    78  		_, _ = fmt.Fprint(Output, s)
    79  		logToSecondaryOutput(a...)
    80  
    81  		currentProgressSpinner, _ = currentProgressSpinner.Start(currentProgressSpinner.Text)
    82  		return
    83  	}
    84  
    85  	_, _ = fmt.Fprint(Output, s)
    86  	logToSecondaryOutput(a...)
    87  }
    88  
    89  func logToSecondaryOutput(a ...any) {
    90  	if VerboseSecondaryOutput == nil {
    91  		// Do nothing if this is not set
    92  		return
    93  	}
    94  
    95  	s := fmt.Sprint(a...)
    96  	if len(s) == 0 || s[len(s)-1] != '\n' {
    97  		s += "\n"
    98  	}
    99  
   100  	s = pterm.RemoveColorFromString(s)
   101  	_, _ = fmt.Fprint(VerboseSecondaryOutput, s)
   102  }
   103  
   104  // Successf highlights a message as successful
   105  func Successf(format string, a ...any) {
   106  	Success(fmt.Sprintf(format, a...))
   107  }
   108  
   109  func Success(a ...any) {
   110  	log(pterm.Style{pterm.FgGreen}, "✅ ", a...)
   111  }
   112  
   113  // Warnf highlights a message as a warning
   114  func Warnf(format string, a ...any) {
   115  	Warn(fmt.Sprintf(format, a...))
   116  }
   117  
   118  func Warn(a ...any) {
   119  	log(pterm.Style{pterm.Bold, pterm.FgYellow}, "⚠️", a...)
   120  }
   121  
   122  // Notef highlights a message as a note
   123  func Notef(format string, a ...any) {
   124  	Note(fmt.Sprintf(format, a...))
   125  }
   126  
   127  func Note(a ...any) {
   128  	log(pterm.Style{pterm.FgLightYellow}, "", a...)
   129  }
   130  
   131  // Errorf highlights a message as an error and shows the stack strace if the --verbose flag is active
   132  func Errorf(err error, format string, a ...any) {
   133  	Error(err, fmt.Sprintf(format, a...))
   134  }
   135  
   136  func Error(err error, a ...any) {
   137  	// If no message is provided, print the message of the error
   138  	if len(a) == 0 {
   139  		a = []any{err.Error()}
   140  	}
   141  	log(pterm.Style{pterm.Bold, pterm.FgRed}, "❌ ", a...)
   142  
   143  	type stackTracer interface {
   144  		StackTrace() errors.StackTrace
   145  	}
   146  	var st stackTracer
   147  	if errors.As(err, &st) {
   148  		Debugf("%+v", st)
   149  	}
   150  }
   151  
   152  // Infof outputs a regular user message without any highlighting
   153  func Infof(format string, a ...any) {
   154  	Info(fmt.Sprintf(format, a...))
   155  }
   156  
   157  func Info(a ...any) {
   158  	log(pterm.Style{pterm.Fuzzy}, "", a...)
   159  }
   160  
   161  // Debugf outputs additional information when the --verbose flag is active
   162  func Debugf(format string, a ...any) {
   163  	Debug(fmt.Sprintf(format, a...))
   164  }
   165  
   166  func Debug(a ...any) {
   167  	if viper.GetBool("verbose") {
   168  		log(pterm.Style{pterm.Fuzzy}, "🔍 ", a...)
   169  		return
   170  	}
   171  
   172  	// Secondary output catches full verbose log even
   173  	// if it is not called in verbose mode
   174  	logToSecondaryOutput(a...)
   175  }
   176  
   177  // Printf writes without any colors
   178  func Printf(format string, a ...any) {
   179  	Print(fmt.Sprintf(format, a...))
   180  }
   181  
   182  func Print(a ...any) {
   183  	log(pterm.Style{pterm.FgDefault}, "", a...)
   184  }
   185  
   186  func Finding(a ...any) {
   187  	log(pterm.Style{pterm.FgDefault}, "💥 ", a...)
   188  }
   189  
   190  func PlainStyle() bool {
   191  	return viper.GetString("style") == "plain" || viper.GetBool("plain")
   192  }