github.com/amp-space/amp-sdk-go@v0.7.6/stdlib/log/logger.go (about) 1 package log 2 3 import ( 4 "bytes" 5 "flag" 6 "fmt" 7 "os" 8 "os/signal" 9 "strings" 10 "syscall" 11 "time" 12 13 "github.com/brynbellomy/klog" 14 ) 15 16 // Formatter specifies how each log entry header should be formatted.= 17 type Formatter interface { 18 FormatHeader(severity string, filename string, lineNum int, ioBuf *bytes.Buffer) 19 } 20 21 // Logger abstracts basic logging functions. 22 type Logger interface { 23 SetLogLabel(inLabel string) 24 GetLogLabel() string 25 GetLogPrefix() string 26 Debug(args ...interface{}) 27 Debugf(inFormat string, args ...interface{}) 28 Debugw(inFormat string, fields Fields) 29 Success(args ...interface{}) 30 Successf(inFormat string, args ...interface{}) 31 Successw(inFormat string, fields Fields) 32 LogV(inVerboseLevel int32) bool 33 Info(inVerboseLevel int32, args ...interface{}) 34 Infof(inVerboseLevel int32, inFormat string, args ...interface{}) 35 Infow(inFormat string, fields Fields) 36 Warn(args ...interface{}) 37 Warnf(inFormat string, args ...interface{}) 38 Warnw(inFormat string, fields Fields) 39 Error(args ...interface{}) 40 Errorf(inFormat string, args ...interface{}) 41 Errorw(inFormat string, fields Fields) 42 Fatalf(inFormat string, args ...interface{}) 43 } 44 45 func InitFlags(flagset *flag.FlagSet) { 46 klog.InitFlags(flagset) 47 } 48 49 // Uses the stock log formatter with the given settings 50 func UseStockFormatter(fileNameCharWidth int, useColor bool) { 51 UseFormatter(&klog.FmtConstWidth{ 52 FileNameCharWidth: fileNameCharWidth, 53 UseColor: useColor, 54 }) 55 } 56 57 func UseFormatter(inFormatter Formatter) { 58 klog.SetFormatter(inFormatter) 59 } 60 61 func Flush() { 62 klog.Flush() 63 } 64 65 type logger struct { 66 hasPrefix bool 67 logPrefix string 68 logLabel string 69 } 70 71 var ( 72 gLongestLabel int 73 gSpacing = " " 74 ) 75 76 func (l *logger) Padding() string { 77 labelLen := len(l.logLabel) 78 if labelLen >= gLongestLabel { 79 return " " 80 } else { 81 return gSpacing[:gLongestLabel-labelLen] 82 } 83 } 84 85 // NewLogger creates and inits a new Logger with the given label. 86 func NewLogger(label string) Logger { 87 l := &logger{} 88 if label != "" { 89 l.SetLogLabel(label) 90 } 91 return l 92 } 93 94 // Fatalf -- see Fatalf (above) 95 func Fatalf(inFormat string, args ...interface{}) { 96 gLogger.Fatalf(inFormat, args...) 97 } 98 99 var gLogger = logger{} 100 101 // SetLogLabel sets the label prefix for all entries logged. 102 func (l *logger) SetLogLabel(inLabel string) { 103 l.logLabel = inLabel 104 l.hasPrefix = len(inLabel) > 0 105 if l.hasPrefix { 106 l.logPrefix = fmt.Sprintf("[%s] ", inLabel) 107 108 // Find length of longest line 109 { 110 longest := gLongestLabel 111 max := len(gSpacing) - 1 112 N := len(l.logPrefix) 113 for pos := 0; pos < N; { 114 lineEnd := strings.IndexByte(l.logPrefix[pos:], '\n') 115 if lineEnd < 0 { 116 pos = N 117 } 118 lineLen := min(max, 1+lineEnd-pos) 119 if lineLen > longest { 120 longest = lineLen 121 gLongestLabel = longest 122 } 123 pos += lineEnd + 1 124 } 125 } 126 } 127 } 128 129 // GetLogLabel returns the label last set via SetLogLabel() 130 func (l *logger) GetLogLabel() string { 131 return l.logLabel 132 } 133 134 // GetLogPrefix returns the the text that prefixes all log messages for this context. 135 func (l *logger) GetLogPrefix() string { 136 return l.logPrefix 137 } 138 139 // LogV returns true if logging is currently enabled for log verbose level. 140 func (l *logger) LogV(inVerboseLevel int32) bool { 141 return bool(klog.V(klog.Level(inVerboseLevel))) 142 } 143 144 func (l *logger) Debug(args ...interface{}) { 145 if l.hasPrefix { 146 klog.DebugDepth(1, l.logPrefix, l.Padding(), fmt.Sprint(args...)) 147 } else { 148 klog.DebugDepth(1, args...) 149 } 150 } 151 152 func (l *logger) Debugf(inFormat string, args ...interface{}) { 153 if l.hasPrefix { 154 klog.DebugDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(inFormat, args...)) 155 } else { 156 klog.DebugDepth(1, fmt.Sprintf(inFormat, args...)) 157 } 158 } 159 160 func (l *logger) Debugw(msg string, fields Fields) { 161 if l.hasPrefix { 162 klog.DebugDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(msg+" %v", fields)) 163 } else { 164 klog.DebugDepth(1, fmt.Sprintf(msg+" %v", fields)) 165 } 166 } 167 168 func (l *logger) Success(args ...interface{}) { 169 if l.hasPrefix { 170 klog.SuccessDepth(1, l.logPrefix, l.Padding(), fmt.Sprint(args...)) 171 } else { 172 klog.SuccessDepth(1, args...) 173 } 174 } 175 176 func (l *logger) Successf(inFormat string, args ...interface{}) { 177 if l.hasPrefix { 178 klog.SuccessDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(inFormat, args...)) 179 } else { 180 klog.SuccessDepth(1, fmt.Sprintf(inFormat, args...)) 181 } 182 } 183 184 func (l *logger) Successw(msg string, fields Fields) { 185 if l.hasPrefix { 186 klog.SuccessDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(msg+" %v", fields)) 187 } else { 188 klog.SuccessDepth(1, fmt.Sprintf(msg+" %v", fields)) 189 } 190 } 191 192 // Info logs to the INFO log. 193 // Arguments are handled like fmt.Print(); a newline is appended if missing. 194 // 195 // Verbose level conventions: 196 // 0. Enabled during production and field deployment. Use this for important high-level info. 197 // 1. Enabled during testing and development. Use for high-level changes in state, mode, or connection. 198 // 2. Enabled during low-level debugging and troubleshooting. 199 func (l *logger) Info(inVerboseLevel int32, args ...interface{}) { 200 logIt := true 201 if inVerboseLevel > 0 { 202 logIt = bool(klog.V(klog.Level(inVerboseLevel))) 203 } 204 205 if logIt { 206 if l.hasPrefix { 207 klog.InfoDepth(1, l.logPrefix, l.Padding(), fmt.Sprint(args...)) 208 } else { 209 klog.InfoDepth(1, args...) 210 } 211 } 212 } 213 214 // Infof logs to the INFO log. 215 // Arguments are handled like fmt.Printf(); a newline is appended if missing. 216 // 217 // See comments above for Info() for guidelines for inVerboseLevel. 218 func (l *logger) Infof(inVerboseLevel int32, inFormat string, args ...interface{}) { 219 logIt := true 220 if inVerboseLevel > 0 { 221 logIt = bool(klog.V(klog.Level(inVerboseLevel))) 222 } 223 224 if logIt { 225 if l.hasPrefix { 226 klog.InfoDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(inFormat, args...)) 227 } else { 228 klog.InfoDepth(1, fmt.Sprintf(inFormat, args...)) 229 } 230 } 231 } 232 233 func (l *logger) Infow(msg string, fields Fields) { 234 if l.hasPrefix { 235 klog.InfoDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(msg+" %v", fields)) 236 } else { 237 klog.InfoDepth(1, fmt.Sprintf(msg+" %v", fields)) 238 } 239 } 240 241 // Warn logs to the WARNING and INFO logs. 242 // Arguments are handled like fmt.Print(); a newline is appended if missing. 243 // 244 // Warnings are reserved for situations that indicate an inconsistency or an error that 245 // won't result in a departure of specifications, correctness, or expected behavior. 246 func (l *logger) Warn(args ...interface{}) { 247 if l.hasPrefix { 248 klog.WarningDepth(1, l.logPrefix, l.Padding(), fmt.Sprint(args...)) 249 } else { 250 klog.WarningDepth(1, args...) 251 } 252 } 253 254 // Warnf logs to the WARNING and INFO logs. 255 // Arguments are handled like fmt.Printf(); a newline is appended if missing. 256 // 257 // See comments above for Warn() for guidelines on errors vs warnings. 258 func (l *logger) Warnf(inFormat string, args ...interface{}) { 259 if l.hasPrefix { 260 klog.WarningDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(inFormat, args...)) 261 } else { 262 klog.WarningDepth(1, fmt.Sprintf(inFormat, args...)) 263 } 264 } 265 266 func (l *logger) Warnw(msg string, fields Fields) { 267 if l.hasPrefix { 268 klog.WarningDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(msg+" %v", fields)) 269 } else { 270 klog.WarningDepth(1, fmt.Sprintf(msg+" %v", fields)) 271 } 272 } 273 274 // Error logs to the ERROR, WARNING, and INFO logs. 275 // Arguments are handled like fmt.Print(); a newline is appended if missing. 276 // 277 // Errors are reserved for situations that indicate an implementation deficiency, a 278 // corruption of data or resources, or an issue that if not addressed could spiral into deeper issues. 279 // Logging an error reflects that correctness or expected behavior is either broken or under threat. 280 func (l *logger) Error(args ...interface{}) { 281 { 282 if l.hasPrefix { 283 klog.ErrorDepth(1, l.logPrefix, l.Padding(), fmt.Sprint(args...)) 284 } else { 285 klog.ErrorDepth(1, args...) 286 } 287 } 288 } 289 290 // Errorf logs to the ERROR, WARNING, and INFO logs. 291 // Arguments are handled like fmt.Print; a newline is appended if missing. 292 // 293 // See comments above for Error() for guidelines on errors vs warnings. 294 func (l *logger) Errorf(inFormat string, args ...interface{}) { 295 { 296 if l.hasPrefix { 297 klog.ErrorDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(inFormat, args...)) 298 } else { 299 klog.ErrorDepth(1, fmt.Sprintf(inFormat, args...)) 300 } 301 } 302 } 303 304 func (l *logger) Errorw(msg string, fields Fields) { 305 if l.hasPrefix { 306 klog.ErrorDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(msg+" %v", fields)) 307 } else { 308 klog.ErrorDepth(1, fmt.Sprintf(msg+" %v", fields)) 309 } 310 } 311 312 // Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs, 313 // Arguments are handled like fmt.Printf(); a newline is appended if missing. 314 func (l *logger) Fatalf(inFormat string, args ...interface{}) { 315 { 316 if l.hasPrefix { 317 klog.FatalDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(inFormat, args...)) 318 } else { 319 klog.FatalDepth(1, fmt.Sprintf(inFormat, args...)) 320 } 321 } 322 } 323 324 func AwaitInterrupt() ( 325 first <-chan struct{}, 326 repeated <-chan struct{}, 327 ) { 328 onFirst := make(chan struct{}) 329 onRepeated := make(chan struct{}) 330 331 go func() { 332 sigInbox := make(chan os.Signal, 1) 333 334 signal.Notify(sigInbox, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) 335 336 count := 0 337 firstTime := int64(0) 338 339 for sig := range sigInbox { 340 count++ 341 curTime := time.Now().Unix() 342 343 // Prevent un-terminated ^c character in terminal 344 fmt.Println() 345 346 klog.WarningDepth(1, "Received ", sig.String(), "\n") 347 348 if onFirst != nil { 349 firstTime = curTime 350 close(onFirst) 351 onFirst = nil 352 } else if onRepeated != nil { 353 if curTime > firstTime+3 && count >= 3 { 354 klog.WarningDepth(1, "Received repeated interrupts\n") 355 klog.Flush() 356 close(onRepeated) 357 onRepeated = nil 358 } 359 } 360 } 361 }() 362 363 klog.InfoDepth(1, "To stop: \x1b[1m^C\x1b[0m or \x1b[1mkill -s SIGINT ", os.Getpid(), "\x1b[0m") 364 return onFirst, onRepeated 365 } 366 367 func min(a, b int) int { 368 if a < b { 369 return a 370 } 371 return b 372 }