| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | package log | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2017-02-20 18:05:15 +02:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/go-stack/stack" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const timeKey = "t" | 
					
						
							|  |  |  | const lvlKey = "lvl" | 
					
						
							|  |  |  | const msgKey = "msg" | 
					
						
							| 
									
										
										
											
												cmd, dashboard, log: log collection and exploration (#17097)
* cmd, dashboard, internal, log, node: logging feature
* cmd, dashboard, internal, log: requested changes
* dashboard, vendor: gofmt, govendor, use vendored file watcher
* dashboard, log: gofmt -s -w, goimports
* dashboard, log: gosimple
											
										 
											2018-07-11 10:59:04 +03:00
										 |  |  | const ctxKey = "ctx" | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | const errorKey = "LOG15_ERROR" | 
					
						
							| 
									
										
										
										
											2018-06-14 11:21:17 +02:00
										 |  |  | const skipLevel = 2 | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | type Lvl int | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	LvlCrit Lvl = iota | 
					
						
							|  |  |  | 	LvlError | 
					
						
							|  |  |  | 	LvlWarn | 
					
						
							|  |  |  | 	LvlInfo | 
					
						
							|  |  |  | 	LvlDebug | 
					
						
							| 
									
										
										
										
											2017-02-20 18:05:15 +02:00
										 |  |  | 	LvlTrace | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-22 00:28:43 -07:00
										 |  |  | // AlignedString returns a 5-character string containing the name of a Lvl. | 
					
						
							| 
									
										
										
										
											2017-02-23 18:30:32 +01:00
										 |  |  | func (l Lvl) AlignedString() string { | 
					
						
							|  |  |  | 	switch l { | 
					
						
							|  |  |  | 	case LvlTrace: | 
					
						
							|  |  |  | 		return "TRACE" | 
					
						
							|  |  |  | 	case LvlDebug: | 
					
						
							|  |  |  | 		return "DEBUG" | 
					
						
							|  |  |  | 	case LvlInfo: | 
					
						
							|  |  |  | 		return "INFO " | 
					
						
							|  |  |  | 	case LvlWarn: | 
					
						
							|  |  |  | 		return "WARN " | 
					
						
							|  |  |  | 	case LvlError: | 
					
						
							|  |  |  | 		return "ERROR" | 
					
						
							|  |  |  | 	case LvlCrit: | 
					
						
							|  |  |  | 		return "CRIT " | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		panic("bad level") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Strings returns the name of a Lvl. | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | func (l Lvl) String() string { | 
					
						
							|  |  |  | 	switch l { | 
					
						
							| 
									
										
										
										
											2017-02-20 18:05:15 +02:00
										 |  |  | 	case LvlTrace: | 
					
						
							|  |  |  | 		return "trce" | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | 	case LvlDebug: | 
					
						
							|  |  |  | 		return "dbug" | 
					
						
							|  |  |  | 	case LvlInfo: | 
					
						
							|  |  |  | 		return "info" | 
					
						
							|  |  |  | 	case LvlWarn: | 
					
						
							|  |  |  | 		return "warn" | 
					
						
							|  |  |  | 	case LvlError: | 
					
						
							|  |  |  | 		return "eror" | 
					
						
							|  |  |  | 	case LvlCrit: | 
					
						
							|  |  |  | 		return "crit" | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		panic("bad level") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-22 00:28:43 -07:00
										 |  |  | // LvlFromString returns the appropriate Lvl from a string name. | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | // Useful for parsing command line args and configuration files. | 
					
						
							|  |  |  | func LvlFromString(lvlString string) (Lvl, error) { | 
					
						
							|  |  |  | 	switch lvlString { | 
					
						
							| 
									
										
										
										
											2017-02-20 18:05:15 +02:00
										 |  |  | 	case "trace", "trce": | 
					
						
							|  |  |  | 		return LvlTrace, nil | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | 	case "debug", "dbug": | 
					
						
							|  |  |  | 		return LvlDebug, nil | 
					
						
							|  |  |  | 	case "info": | 
					
						
							|  |  |  | 		return LvlInfo, nil | 
					
						
							|  |  |  | 	case "warn": | 
					
						
							|  |  |  | 		return LvlWarn, nil | 
					
						
							|  |  |  | 	case "error", "eror": | 
					
						
							|  |  |  | 		return LvlError, nil | 
					
						
							|  |  |  | 	case "crit": | 
					
						
							|  |  |  | 		return LvlCrit, nil | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return LvlDebug, fmt.Errorf("Unknown level: %v", lvlString) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // A Record is what a Logger asks its handler to write | 
					
						
							|  |  |  | type Record struct { | 
					
						
							|  |  |  | 	Time     time.Time | 
					
						
							|  |  |  | 	Lvl      Lvl | 
					
						
							|  |  |  | 	Msg      string | 
					
						
							|  |  |  | 	Ctx      []interface{} | 
					
						
							|  |  |  | 	Call     stack.Call | 
					
						
							|  |  |  | 	KeyNames RecordKeyNames | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-22 00:28:43 -07:00
										 |  |  | // RecordKeyNames gets stored in a Record when the write function is executed. | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | type RecordKeyNames struct { | 
					
						
							|  |  |  | 	Time string | 
					
						
							|  |  |  | 	Msg  string | 
					
						
							|  |  |  | 	Lvl  string | 
					
						
							| 
									
										
										
											
												cmd, dashboard, log: log collection and exploration (#17097)
* cmd, dashboard, internal, log, node: logging feature
* cmd, dashboard, internal, log: requested changes
* dashboard, vendor: gofmt, govendor, use vendored file watcher
* dashboard, log: gofmt -s -w, goimports
* dashboard, log: gosimple
											
										 
											2018-07-11 10:59:04 +03:00
										 |  |  | 	Ctx  string | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // A Logger writes key/value pairs to a Handler | 
					
						
							|  |  |  | type Logger interface { | 
					
						
							|  |  |  | 	// New returns a new Logger that has this logger's context plus the given context | 
					
						
							|  |  |  | 	New(ctx ...interface{}) Logger | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// GetHandler gets the handler associated with the logger. | 
					
						
							|  |  |  | 	GetHandler() Handler | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// SetHandler updates the logger to write records to the specified handler. | 
					
						
							|  |  |  | 	SetHandler(h Handler) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Log a message at the given level with context key/value pairs | 
					
						
							| 
									
										
										
										
											2017-02-20 18:05:15 +02:00
										 |  |  | 	Trace(msg string, ctx ...interface{}) | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | 	Debug(msg string, ctx ...interface{}) | 
					
						
							|  |  |  | 	Info(msg string, ctx ...interface{}) | 
					
						
							|  |  |  | 	Warn(msg string, ctx ...interface{}) | 
					
						
							|  |  |  | 	Error(msg string, ctx ...interface{}) | 
					
						
							|  |  |  | 	Crit(msg string, ctx ...interface{}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type logger struct { | 
					
						
							|  |  |  | 	ctx []interface{} | 
					
						
							|  |  |  | 	h   *swapHandler | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-14 11:21:17 +02:00
										 |  |  | func (l *logger) write(msg string, lvl Lvl, ctx []interface{}, skip int) { | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | 	l.h.Log(&Record{ | 
					
						
							|  |  |  | 		Time: time.Now(), | 
					
						
							|  |  |  | 		Lvl:  lvl, | 
					
						
							|  |  |  | 		Msg:  msg, | 
					
						
							|  |  |  | 		Ctx:  newContext(l.ctx, ctx), | 
					
						
							| 
									
										
										
										
											2018-06-14 11:21:17 +02:00
										 |  |  | 		Call: stack.Caller(skip), | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | 		KeyNames: RecordKeyNames{ | 
					
						
							|  |  |  | 			Time: timeKey, | 
					
						
							|  |  |  | 			Msg:  msgKey, | 
					
						
							|  |  |  | 			Lvl:  lvlKey, | 
					
						
							| 
									
										
										
											
												cmd, dashboard, log: log collection and exploration (#17097)
* cmd, dashboard, internal, log, node: logging feature
* cmd, dashboard, internal, log: requested changes
* dashboard, vendor: gofmt, govendor, use vendored file watcher
* dashboard, log: gofmt -s -w, goimports
* dashboard, log: gosimple
											
										 
											2018-07-11 10:59:04 +03:00
										 |  |  | 			Ctx:  ctxKey, | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l *logger) New(ctx ...interface{}) Logger { | 
					
						
							|  |  |  | 	child := &logger{newContext(l.ctx, ctx), new(swapHandler)} | 
					
						
							|  |  |  | 	child.SetHandler(l.h) | 
					
						
							|  |  |  | 	return child | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newContext(prefix []interface{}, suffix []interface{}) []interface{} { | 
					
						
							|  |  |  | 	normalizedSuffix := normalize(suffix) | 
					
						
							|  |  |  | 	newCtx := make([]interface{}, len(prefix)+len(normalizedSuffix)) | 
					
						
							|  |  |  | 	n := copy(newCtx, prefix) | 
					
						
							|  |  |  | 	copy(newCtx[n:], normalizedSuffix) | 
					
						
							|  |  |  | 	return newCtx | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-20 18:05:15 +02:00
										 |  |  | func (l *logger) Trace(msg string, ctx ...interface{}) { | 
					
						
							| 
									
										
										
										
											2018-06-14 11:21:17 +02:00
										 |  |  | 	l.write(msg, LvlTrace, ctx, skipLevel) | 
					
						
							| 
									
										
										
										
											2017-02-20 18:05:15 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | func (l *logger) Debug(msg string, ctx ...interface{}) { | 
					
						
							| 
									
										
										
										
											2018-06-14 11:21:17 +02:00
										 |  |  | 	l.write(msg, LvlDebug, ctx, skipLevel) | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l *logger) Info(msg string, ctx ...interface{}) { | 
					
						
							| 
									
										
										
										
											2018-06-14 11:21:17 +02:00
										 |  |  | 	l.write(msg, LvlInfo, ctx, skipLevel) | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l *logger) Warn(msg string, ctx ...interface{}) { | 
					
						
							| 
									
										
										
										
											2018-06-14 11:21:17 +02:00
										 |  |  | 	l.write(msg, LvlWarn, ctx, skipLevel) | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l *logger) Error(msg string, ctx ...interface{}) { | 
					
						
							| 
									
										
										
										
											2018-06-14 11:21:17 +02:00
										 |  |  | 	l.write(msg, LvlError, ctx, skipLevel) | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l *logger) Crit(msg string, ctx ...interface{}) { | 
					
						
							| 
									
										
										
										
											2018-06-14 11:21:17 +02:00
										 |  |  | 	l.write(msg, LvlCrit, ctx, skipLevel) | 
					
						
							| 
									
										
										
										
											2017-02-20 18:05:15 +02:00
										 |  |  | 	os.Exit(1) | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l *logger) GetHandler() Handler { | 
					
						
							|  |  |  | 	return l.h.Get() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l *logger) SetHandler(h Handler) { | 
					
						
							|  |  |  | 	l.h.Swap(h) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func normalize(ctx []interface{}) []interface{} { | 
					
						
							|  |  |  | 	// if the caller passed a Ctx object, then expand it | 
					
						
							|  |  |  | 	if len(ctx) == 1 { | 
					
						
							|  |  |  | 		if ctxMap, ok := ctx[0].(Ctx); ok { | 
					
						
							|  |  |  | 			ctx = ctxMap.toArray() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// ctx needs to be even because it's a series of key/value pairs | 
					
						
							|  |  |  | 	// no one wants to check for errors on logging functions, | 
					
						
							|  |  |  | 	// so instead of erroring on bad input, we'll just make sure | 
					
						
							|  |  |  | 	// that things are the right length and users can fix bugs | 
					
						
							|  |  |  | 	// when they see the output looks wrong | 
					
						
							|  |  |  | 	if len(ctx)%2 != 0 { | 
					
						
							|  |  |  | 		ctx = append(ctx, nil, errorKey, "Normalized odd number of arguments by adding nil") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ctx | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Lazy allows you to defer calculation of a logged value that is expensive | 
					
						
							|  |  |  | // to compute until it is certain that it must be evaluated with the given filters. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Lazy may also be used in conjunction with a Logger's New() function | 
					
						
							|  |  |  | // to generate a child logger which always reports the current value of changing | 
					
						
							|  |  |  | // state. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You may wrap any function which takes no arguments to Lazy. It may return any | 
					
						
							|  |  |  | // number of values of any type. | 
					
						
							|  |  |  | type Lazy struct { | 
					
						
							|  |  |  | 	Fn interface{} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Ctx is a map of key/value pairs to pass as context to a log function | 
					
						
							|  |  |  | // Use this only if you really need greater safety around the arguments you pass | 
					
						
							|  |  |  | // to the logging functions. | 
					
						
							|  |  |  | type Ctx map[string]interface{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (c Ctx) toArray() []interface{} { | 
					
						
							|  |  |  | 	arr := make([]interface{}, len(c)*2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	i := 0 | 
					
						
							|  |  |  | 	for k, v := range c { | 
					
						
							|  |  |  | 		arr[i] = k | 
					
						
							|  |  |  | 		arr[i+1] = v | 
					
						
							|  |  |  | 		i += 2 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return arr | 
					
						
							|  |  |  | } |