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 }