github.com/nycdavid/zeus@v0.0.0-20201208104106-9ba439429e03/go/shinylog/shinylog.go (about) 1 package shinylog 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 "os" 8 "strings" 9 "sync" 10 "syscall" 11 ) 12 13 type ShinyLogger struct { 14 mu sync.Mutex 15 errorLogger log.Logger 16 locationErrorLogger log.Logger 17 suppressOutput bool 18 disableColor bool 19 } 20 21 type loggerOptions struct { 22 isError, printNewline, includeLocation bool 23 } 24 25 var errorOptions loggerOptions 26 var stdoutOptions loggerOptions 27 var stderrOptions loggerOptions 28 29 func init() { 30 errorOptions = loggerOptions{isError: true, printNewline: true, includeLocation: true} 31 stdoutOptions = loggerOptions{isError: false, printNewline: true, includeLocation: false} 32 stderrOptions = loggerOptions{isError: false, printNewline: true, includeLocation: false} 33 } 34 35 func NewShinyLogger(out, err interface { 36 io.Writer 37 }) *ShinyLogger { 38 errorLogger := log.New(err, "", 0) 39 locationErrorLogger := log.New(err, "", log.Lshortfile) 40 var mu sync.Mutex 41 return &ShinyLogger{mu, *errorLogger, *locationErrorLogger, false, false} 42 } 43 44 func NewTraceLogger(out interface { 45 io.Writer 46 }) *log.Logger { 47 return log.New(out, "", log.Ldate|log.Ltime|log.Lmicroseconds) 48 } 49 50 const ( 51 red = "\x1b[31m" 52 green = "\x1b[32m" 53 brightgreen = "\x1b[1;32m" 54 yellow = "\x1b[33m" 55 blue = "\x1b[34m" 56 magenta = "\x1b[35m" 57 reset = "\x1b[0m" 58 ) 59 60 var dlm sync.RWMutex 61 var defaultLogger *ShinyLogger = NewShinyLogger(os.Stdout, os.Stderr) 62 var traceLogger *log.Logger = nil 63 64 func DefaultLogger() *ShinyLogger { 65 dlm.RLock() 66 defer dlm.RUnlock() 67 return defaultLogger 68 } 69 70 func SetDefaultLogger(sl *ShinyLogger) { 71 dlm.Lock() 72 defaultLogger = sl 73 dlm.Unlock() 74 } 75 76 func TraceLogger() *log.Logger { 77 dlm.RLock() 78 defer dlm.RUnlock() 79 return traceLogger 80 } 81 82 func SetTraceLogger(sl *log.Logger) { 83 dlm.Lock() 84 traceLogger = sl 85 dlm.Unlock() 86 } 87 88 func Suppress() { DefaultLogger().Suppress() } 89 func DisableColor() { DefaultLogger().DisableColor() } 90 func Colorized(msg string) (printed bool) { return DefaultLogger().Colorized(msg) } 91 func Error(err error) bool { return DefaultLogger().Error(err) } 92 func FatalError(err error) { DefaultLogger().FatalError(err) } 93 func FatalErrorString(msg string) { DefaultLogger().FatalErrorString(msg) } 94 func ErrorString(msg string) bool { return DefaultLogger().ErrorString(msg) } 95 func StdErrorString(msg string) bool { return DefaultLogger().StdErrorString(msg) } 96 func Red(msg string) bool { return DefaultLogger().Red(msg) } 97 func Green(msg string) bool { return DefaultLogger().Green(msg) } 98 func Brightgreen(msg string) bool { return DefaultLogger().Brightgreen(msg) } 99 func Yellow(msg string) bool { return DefaultLogger().Yellow(msg) } 100 func Blue(msg string) bool { return DefaultLogger().Blue(msg) } 101 func Magenta(msg string) bool { return DefaultLogger().Magenta(msg) } 102 103 func TraceEnabled() bool { 104 return TraceLogger() != nil 105 } 106 107 func Trace(format string, v ...interface{}) bool { 108 if TraceEnabled() { 109 TraceLogger().Printf(format, v...) 110 return true 111 } 112 return false 113 } 114 115 func (l *ShinyLogger) Suppress() { 116 l.mu.Lock() 117 defer l.mu.Unlock() 118 l.suppressOutput = true 119 } 120 121 func (l *ShinyLogger) DisableColor() { 122 l.disableColor = true 123 } 124 125 func (l *ShinyLogger) Colorized(msg string) (printed bool) { 126 return l.colorized(3, msg, stdoutOptions) 127 } 128 129 func (l *ShinyLogger) ColorizedSansNl(msg string) (printed bool) { 130 return l.colorized(3, msg, loggerOptions{isError: false, printNewline: false, includeLocation: false}) 131 } 132 133 // If we send SIGTERM rather than explicitly exiting, 134 // the signal can be handled and the master can clean up. 135 // This is a workaround for Go not having `atexit` :(. 136 func terminate() { 137 proc, _ := os.FindProcess(os.Getpid()) 138 proc.Signal(syscall.SIGTERM) 139 } 140 141 func (l *ShinyLogger) FatalErrorString(msg string) { 142 l.colorized(3, "{red}"+msg, errorOptions) 143 terminate() 144 } 145 146 func (l *ShinyLogger) FatalError(err error) { 147 l.colorized(3, "{red}"+err.Error(), errorOptions) 148 terminate() 149 } 150 151 func (l *ShinyLogger) Error(err error) bool { 152 return l.colorized(3, "{red}"+err.Error(), errorOptions) 153 } 154 155 func (l *ShinyLogger) ErrorString(msg string) bool { 156 return l.colorized(3, "{red}"+msg, errorOptions) 157 } 158 159 func (l *ShinyLogger) StdErrorString(msg string) bool { 160 return l.colorized(3, "{red}"+msg, stderrOptions) 161 } 162 163 func (l *ShinyLogger) Red(msg string) bool { 164 return l.colorized(3, "{red}"+msg, stdoutOptions) 165 } 166 167 func (l *ShinyLogger) Green(msg string) bool { 168 return l.colorized(3, "{green}"+msg, stdoutOptions) 169 } 170 171 func (l *ShinyLogger) Brightgreen(msg string) bool { 172 return l.colorized(3, "{brightgreen}"+msg, stdoutOptions) 173 } 174 175 func (l *ShinyLogger) Yellow(msg string) bool { 176 return l.colorized(3, "{yellow}"+msg, stdoutOptions) 177 } 178 179 func (l *ShinyLogger) Blue(msg string) bool { 180 return l.colorized(3, "{blue}"+msg, stdoutOptions) 181 } 182 183 func (l *ShinyLogger) Magenta(msg string) bool { 184 return l.colorized(3, "{magenta}"+msg, stdoutOptions) 185 } 186 187 func (l *ShinyLogger) formatColors(msg string) string { 188 if l.disableColor { 189 msg = strings.Replace(msg, "{red}", "", -1) 190 msg = strings.Replace(msg, "{green}", "", -1) 191 msg = strings.Replace(msg, "{brightgreen}", "", -1) 192 msg = strings.Replace(msg, "{yellow}", "", -1) 193 msg = strings.Replace(msg, "{blue}", "", -1) 194 msg = strings.Replace(msg, "{magenta}", "", -1) 195 msg = strings.Replace(msg, "{reset}", "", -1) 196 } else { 197 msg = strings.Replace(msg, "{red}", red, -1) 198 msg = strings.Replace(msg, "{green}", green, -1) 199 msg = strings.Replace(msg, "{brightgreen}", brightgreen, -1) 200 msg = strings.Replace(msg, "{yellow}", yellow, -1) 201 msg = strings.Replace(msg, "{blue}", blue, -1) 202 msg = strings.Replace(msg, "{magenta}", magenta, -1) 203 msg = strings.Replace(msg, "{reset}", reset, -1) 204 } 205 return msg 206 } 207 208 func (l *ShinyLogger) colorized(callDepth int, msg string, options loggerOptions) bool { 209 l.mu.Lock() 210 defer l.mu.Unlock() 211 212 if !l.suppressOutput { 213 msg = l.formatColors(msg) 214 215 if l == DefaultLogger() { 216 callDepth += 1 // this was called through a proxy method 217 } 218 if options.isError { 219 if options.includeLocation { 220 l.locationErrorLogger.Output(callDepth, msg+reset) 221 } else { 222 l.errorLogger.Output(callDepth, msg+reset) 223 } 224 } else { 225 if options.printNewline { 226 fmt.Println(msg + reset) 227 } else { 228 fmt.Print(msg + reset) 229 } 230 } 231 } 232 return !l.suppressOutput 233 }