| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | package log | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"net" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"reflect" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
											
												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
										 |  |  | 	"io/ioutil" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"regexp" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | 	"github.com/go-stack/stack" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-22 00:28:43 -07:00
										 |  |  | // Handler defines where and how log records are written. | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | // A Logger prints its log records by writing to a Handler. | 
					
						
							|  |  |  | // Handlers are composable, providing you great flexibility in combining | 
					
						
							|  |  |  | // them to achieve the logging structure that suits your applications. | 
					
						
							|  |  |  | type Handler interface { | 
					
						
							|  |  |  | 	Log(r *Record) error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // FuncHandler returns a Handler that logs records with the given | 
					
						
							|  |  |  | // function. | 
					
						
							|  |  |  | func FuncHandler(fn func(r *Record) error) Handler { | 
					
						
							|  |  |  | 	return funcHandler(fn) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type funcHandler func(r *Record) error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (h funcHandler) Log(r *Record) error { | 
					
						
							|  |  |  | 	return h(r) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // StreamHandler writes log records to an io.Writer | 
					
						
							|  |  |  | // with the given format. StreamHandler can be used | 
					
						
							|  |  |  | // to easily begin writing log records to other | 
					
						
							|  |  |  | // outputs. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // StreamHandler wraps itself with LazyHandler and SyncHandler | 
					
						
							|  |  |  | // to evaluate Lazy objects and perform safe concurrent writes. | 
					
						
							|  |  |  | func StreamHandler(wr io.Writer, fmtr Format) Handler { | 
					
						
							|  |  |  | 	h := FuncHandler(func(r *Record) error { | 
					
						
							|  |  |  | 		_, err := wr.Write(fmtr.Format(r)) | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	return LazyHandler(SyncHandler(h)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SyncHandler can be wrapped around a handler to guarantee that | 
					
						
							|  |  |  | // only a single Log operation can proceed at a time. It's necessary | 
					
						
							|  |  |  | // for thread-safe concurrent writes. | 
					
						
							|  |  |  | func SyncHandler(h Handler) Handler { | 
					
						
							|  |  |  | 	var mu sync.Mutex | 
					
						
							|  |  |  | 	return FuncHandler(func(r *Record) error { | 
					
						
							|  |  |  | 		defer mu.Unlock() | 
					
						
							|  |  |  | 		mu.Lock() | 
					
						
							|  |  |  | 		return h.Log(r) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // FileHandler returns a handler which writes log records to the give file | 
					
						
							|  |  |  | // using the given format. If the path | 
					
						
							|  |  |  | // already exists, FileHandler will append to the given file. If it does not, | 
					
						
							|  |  |  | // FileHandler will create the file with mode 0644. | 
					
						
							|  |  |  | func FileHandler(path string, fmtr Format) (Handler, error) { | 
					
						
							|  |  |  | 	f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return closingHandler{f, StreamHandler(f, fmtr)}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
											
												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
										 |  |  | // countingWriter wraps a WriteCloser object in order to count the written bytes. | 
					
						
							|  |  |  | type countingWriter struct { | 
					
						
							|  |  |  | 	w     io.WriteCloser // the wrapped object | 
					
						
							|  |  |  | 	count uint           // number of bytes written | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Write increments the byte counter by the number of bytes written. | 
					
						
							|  |  |  | // Implements the WriteCloser interface. | 
					
						
							|  |  |  | func (w *countingWriter) Write(p []byte) (n int, err error) { | 
					
						
							|  |  |  | 	n, err = w.w.Write(p) | 
					
						
							|  |  |  | 	w.count += uint(n) | 
					
						
							|  |  |  | 	return n, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Close implements the WriteCloser interface. | 
					
						
							|  |  |  | func (w *countingWriter) Close() error { | 
					
						
							|  |  |  | 	return w.w.Close() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // prepFile opens the log file at the given path, and cuts off the invalid part | 
					
						
							|  |  |  | // from the end, because the previous execution could have been finished by interruption. | 
					
						
							|  |  |  | // Assumes that every line ended by '\n' contains a valid log record. | 
					
						
							|  |  |  | func prepFile(path string) (*countingWriter, error) { | 
					
						
							|  |  |  | 	f, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND, 0600) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	_, err = f.Seek(-1, io.SeekEnd) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	buf := make([]byte, 1) | 
					
						
							|  |  |  | 	var cut int64 | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		if _, err := f.Read(buf); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if buf[0] == '\n' { | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if _, err = f.Seek(-2, io.SeekCurrent); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		cut++ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	fi, err := f.Stat() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ns := fi.Size() - cut | 
					
						
							|  |  |  | 	if err = f.Truncate(ns); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return &countingWriter{w: f, count: uint(ns)}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RotatingFileHandler returns a handler which writes log records to file chunks | 
					
						
							|  |  |  | // at the given path. When a file's size reaches the limit, the handler creates | 
					
						
							|  |  |  | // a new file named after the timestamp of the first log record it will contain. | 
					
						
							|  |  |  | func RotatingFileHandler(path string, limit uint, formatter Format) (Handler, error) { | 
					
						
							|  |  |  | 	if err := os.MkdirAll(path, 0700); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	files, err := ioutil.ReadDir(path) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	re := regexp.MustCompile(`\.log$`) | 
					
						
							|  |  |  | 	last := len(files) - 1 | 
					
						
							|  |  |  | 	for last >= 0 && (!files[last].Mode().IsRegular() || !re.MatchString(files[last].Name())) { | 
					
						
							|  |  |  | 		last-- | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var counter *countingWriter | 
					
						
							|  |  |  | 	if last >= 0 && files[last].Size() < int64(limit) { | 
					
						
							|  |  |  | 		// Open the last file, and continue to write into it until it's size reaches the limit. | 
					
						
							|  |  |  | 		if counter, err = prepFile(filepath.Join(path, files[last].Name())); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if counter == nil { | 
					
						
							|  |  |  | 		counter = new(countingWriter) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	h := StreamHandler(counter, formatter) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return FuncHandler(func(r *Record) error { | 
					
						
							|  |  |  | 		if counter.count > limit { | 
					
						
							|  |  |  | 			counter.Close() | 
					
						
							|  |  |  | 			counter.w = nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if counter.w == nil { | 
					
						
							|  |  |  | 			f, err := os.OpenFile( | 
					
						
							|  |  |  | 				filepath.Join(path, fmt.Sprintf("%s.log", strings.Replace(r.Time.Format("060102150405.00"), ".", "", 1))), | 
					
						
							|  |  |  | 				os.O_CREATE|os.O_APPEND|os.O_WRONLY, | 
					
						
							|  |  |  | 				0600, | 
					
						
							|  |  |  | 			) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			counter.w = f | 
					
						
							|  |  |  | 			counter.count = 0 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return h.Log(r) | 
					
						
							|  |  |  | 	}), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | // NetHandler opens a socket to the given address and writes records | 
					
						
							|  |  |  | // over the connection. | 
					
						
							|  |  |  | func NetHandler(network, addr string, fmtr Format) (Handler, error) { | 
					
						
							|  |  |  | 	conn, err := net.Dial(network, addr) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return closingHandler{conn, StreamHandler(conn, fmtr)}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // XXX: closingHandler is essentially unused at the moment | 
					
						
							|  |  |  | // it's meant for a future time when the Handler interface supports | 
					
						
							|  |  |  | // a possible Close() operation | 
					
						
							|  |  |  | type closingHandler struct { | 
					
						
							|  |  |  | 	io.WriteCloser | 
					
						
							|  |  |  | 	Handler | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (h *closingHandler) Close() error { | 
					
						
							|  |  |  | 	return h.WriteCloser.Close() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CallerFileHandler returns a Handler that adds the line number and file of | 
					
						
							|  |  |  | // the calling function to the context with key "caller". | 
					
						
							|  |  |  | func CallerFileHandler(h Handler) Handler { | 
					
						
							|  |  |  | 	return FuncHandler(func(r *Record) error { | 
					
						
							|  |  |  | 		r.Ctx = append(r.Ctx, "caller", fmt.Sprint(r.Call)) | 
					
						
							|  |  |  | 		return h.Log(r) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CallerFuncHandler returns a Handler that adds the calling function name to | 
					
						
							|  |  |  | // the context with key "fn". | 
					
						
							|  |  |  | func CallerFuncHandler(h Handler) Handler { | 
					
						
							|  |  |  | 	return FuncHandler(func(r *Record) error { | 
					
						
							| 
									
										
										
										
											2017-03-23 15:48:30 +01:00
										 |  |  | 		r.Ctx = append(r.Ctx, "fn", formatCall("%+n", r.Call)) | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | 		return h.Log(r) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-23 15:48:30 +01:00
										 |  |  | // This function is here to please go vet on Go < 1.8. | 
					
						
							|  |  |  | func formatCall(format string, c stack.Call) string { | 
					
						
							|  |  |  | 	return fmt.Sprintf(format, c) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | // CallerStackHandler returns a Handler that adds a stack trace to the context | 
					
						
							|  |  |  | // with key "stack". The stack trace is formated as a space separated list of | 
					
						
							|  |  |  | // call sites inside matching []'s. The most recent call site is listed first. | 
					
						
							|  |  |  | // Each call site is formatted according to format. See the documentation of | 
					
						
							|  |  |  | // package github.com/go-stack/stack for the list of supported formats. | 
					
						
							|  |  |  | func CallerStackHandler(format string, h Handler) Handler { | 
					
						
							|  |  |  | 	return FuncHandler(func(r *Record) error { | 
					
						
							|  |  |  | 		s := stack.Trace().TrimBelow(r.Call).TrimRuntime() | 
					
						
							|  |  |  | 		if len(s) > 0 { | 
					
						
							|  |  |  | 			r.Ctx = append(r.Ctx, "stack", fmt.Sprintf(format, s)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return h.Log(r) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // FilterHandler returns a Handler that only writes records to the | 
					
						
							|  |  |  | // wrapped Handler if the given function evaluates true. For example, | 
					
						
							|  |  |  | // to only log records where the 'err' key is not nil: | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | //    logger.SetHandler(FilterHandler(func(r *Record) bool { | 
					
						
							|  |  |  | //        for i := 0; i < len(r.Ctx); i += 2 { | 
					
						
							|  |  |  | //            if r.Ctx[i] == "err" { | 
					
						
							|  |  |  | //                return r.Ctx[i+1] != nil | 
					
						
							|  |  |  | //            } | 
					
						
							|  |  |  | //        } | 
					
						
							|  |  |  | //        return false | 
					
						
							|  |  |  | //    }, h)) | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | func FilterHandler(fn func(r *Record) bool, h Handler) Handler { | 
					
						
							|  |  |  | 	return FuncHandler(func(r *Record) error { | 
					
						
							|  |  |  | 		if fn(r) { | 
					
						
							|  |  |  | 			return h.Log(r) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MatchFilterHandler returns a Handler that only writes records | 
					
						
							|  |  |  | // to the wrapped Handler if the given key in the logged | 
					
						
							|  |  |  | // context matches the value. For example, to only log records | 
					
						
							|  |  |  | // from your ui package: | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | //    log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler) | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | func MatchFilterHandler(key string, value interface{}, h Handler) Handler { | 
					
						
							|  |  |  | 	return FilterHandler(func(r *Record) (pass bool) { | 
					
						
							|  |  |  | 		switch key { | 
					
						
							|  |  |  | 		case r.KeyNames.Lvl: | 
					
						
							|  |  |  | 			return r.Lvl == value | 
					
						
							|  |  |  | 		case r.KeyNames.Time: | 
					
						
							|  |  |  | 			return r.Time == value | 
					
						
							|  |  |  | 		case r.KeyNames.Msg: | 
					
						
							|  |  |  | 			return r.Msg == value | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for i := 0; i < len(r.Ctx); i += 2 { | 
					
						
							|  |  |  | 			if r.Ctx[i] == key { | 
					
						
							|  |  |  | 				return r.Ctx[i+1] == value | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	}, h) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // LvlFilterHandler returns a Handler that only writes | 
					
						
							|  |  |  | // records which are less than the given verbosity | 
					
						
							|  |  |  | // level to the wrapped Handler. For example, to only | 
					
						
							|  |  |  | // log Error/Crit records: | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | //     log.LvlFilterHandler(log.LvlError, log.StdoutHandler) | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | func LvlFilterHandler(maxLvl Lvl, h Handler) Handler { | 
					
						
							|  |  |  | 	return FilterHandler(func(r *Record) (pass bool) { | 
					
						
							|  |  |  | 		return r.Lvl <= maxLvl | 
					
						
							|  |  |  | 	}, h) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-22 00:28:43 -07:00
										 |  |  | // MultiHandler dispatches any write to each of its handlers. | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | // This is useful for writing different types of log information | 
					
						
							|  |  |  | // to different locations. For example, to log to a file and | 
					
						
							|  |  |  | // standard error: | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | //     log.MultiHandler( | 
					
						
							|  |  |  | //         log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), | 
					
						
							|  |  |  | //         log.StderrHandler) | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | func MultiHandler(hs ...Handler) Handler { | 
					
						
							|  |  |  | 	return FuncHandler(func(r *Record) error { | 
					
						
							|  |  |  | 		for _, h := range hs { | 
					
						
							|  |  |  | 			// what to do about failures? | 
					
						
							|  |  |  | 			h.Log(r) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-22 00:28:43 -07:00
										 |  |  | // FailoverHandler writes all log records to the first handler | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | // specified, but will failover and write to the second handler if | 
					
						
							|  |  |  | // the first handler has failed, and so on for all handlers specified. | 
					
						
							|  |  |  | // For example you might want to log to a network socket, but failover | 
					
						
							|  |  |  | // to writing to a file if the network fails, and then to | 
					
						
							|  |  |  | // standard out if the file write fails: | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | //     log.FailoverHandler( | 
					
						
							| 
									
										
										
										
											2018-05-22 00:28:43 -07:00
										 |  |  | //         log.Must.NetHandler("tcp", ":9090", log.JSONFormat()), | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | //         log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), | 
					
						
							|  |  |  | //         log.StdoutHandler) | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // All writes that do not go to the first handler will add context with keys of | 
					
						
							|  |  |  | // the form "failover_err_{idx}" which explain the error encountered while | 
					
						
							|  |  |  | // trying to write to the handlers before them in the list. | 
					
						
							|  |  |  | func FailoverHandler(hs ...Handler) Handler { | 
					
						
							|  |  |  | 	return FuncHandler(func(r *Record) error { | 
					
						
							|  |  |  | 		var err error | 
					
						
							|  |  |  | 		for i, h := range hs { | 
					
						
							|  |  |  | 			err = h.Log(r) | 
					
						
							|  |  |  | 			if err == nil { | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-05-03 01:30:12 -07:00
										 |  |  | 			r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err) | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ChannelHandler writes all records to the given channel. | 
					
						
							|  |  |  | // It blocks if the channel is full. Useful for async processing | 
					
						
							|  |  |  | // of log messages, it's used by BufferedHandler. | 
					
						
							|  |  |  | func ChannelHandler(recs chan<- *Record) Handler { | 
					
						
							|  |  |  | 	return FuncHandler(func(r *Record) error { | 
					
						
							|  |  |  | 		recs <- r | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // BufferedHandler writes all records to a buffered | 
					
						
							|  |  |  | // channel of the given size which flushes into the wrapped | 
					
						
							|  |  |  | // handler whenever it is available for writing. Since these | 
					
						
							|  |  |  | // writes happen asynchronously, all writes to a BufferedHandler | 
					
						
							|  |  |  | // never return an error and any errors from the wrapped handler are ignored. | 
					
						
							|  |  |  | func BufferedHandler(bufSize int, h Handler) Handler { | 
					
						
							|  |  |  | 	recs := make(chan *Record, bufSize) | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		for m := range recs { | 
					
						
							|  |  |  | 			_ = h.Log(m) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	return ChannelHandler(recs) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // LazyHandler writes all values to the wrapped handler after evaluating | 
					
						
							|  |  |  | // any lazy functions in the record's context. It is already wrapped | 
					
						
							|  |  |  | // around StreamHandler and SyslogHandler in this library, you'll only need | 
					
						
							|  |  |  | // it if you write your own Handler. | 
					
						
							|  |  |  | func LazyHandler(h Handler) Handler { | 
					
						
							|  |  |  | 	return FuncHandler(func(r *Record) error { | 
					
						
							|  |  |  | 		// go through the values (odd indices) and reassign | 
					
						
							|  |  |  | 		// the values of any lazy fn to the result of its execution | 
					
						
							|  |  |  | 		hadErr := false | 
					
						
							|  |  |  | 		for i := 1; i < len(r.Ctx); i += 2 { | 
					
						
							|  |  |  | 			lz, ok := r.Ctx[i].(Lazy) | 
					
						
							|  |  |  | 			if ok { | 
					
						
							|  |  |  | 				v, err := evaluateLazy(lz) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					hadErr = true | 
					
						
							|  |  |  | 					r.Ctx[i] = err | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					if cs, ok := v.(stack.CallStack); ok { | 
					
						
							|  |  |  | 						v = cs.TrimBelow(r.Call).TrimRuntime() | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					r.Ctx[i] = v | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if hadErr { | 
					
						
							|  |  |  | 			r.Ctx = append(r.Ctx, errorKey, "bad lazy") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return h.Log(r) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func evaluateLazy(lz Lazy) (interface{}, error) { | 
					
						
							|  |  |  | 	t := reflect.TypeOf(lz.Fn) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if t.Kind() != reflect.Func { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if t.NumIn() > 0 { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if t.NumOut() == 0 { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	value := reflect.ValueOf(lz.Fn) | 
					
						
							|  |  |  | 	results := value.Call([]reflect.Value{}) | 
					
						
							|  |  |  | 	if len(results) == 1 { | 
					
						
							|  |  |  | 		return results[0].Interface(), nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-05-03 01:30:12 -07:00
										 |  |  | 	values := make([]interface{}, len(results)) | 
					
						
							|  |  |  | 	for i, v := range results { | 
					
						
							|  |  |  | 		values[i] = v.Interface() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return values, nil | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DiscardHandler reports success for all writes but does nothing. | 
					
						
							|  |  |  | // It is useful for dynamically disabling logging at runtime via | 
					
						
							|  |  |  | // a Logger's SetHandler method. | 
					
						
							|  |  |  | func DiscardHandler() Handler { | 
					
						
							|  |  |  | 	return FuncHandler(func(r *Record) error { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-22 00:28:43 -07:00
										 |  |  | // Must provides the following Handler creation functions | 
					
						
							| 
									
										
										
										
											2017-02-20 17:39:36 +02:00
										 |  |  | // which instead of returning an error parameter only return a Handler | 
					
						
							|  |  |  | // and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler | 
					
						
							|  |  |  | var Must muster | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func must(h Handler, err error) Handler { | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		panic(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return h | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type muster struct{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (m muster) FileHandler(path string, fmtr Format) Handler { | 
					
						
							|  |  |  | 	return must(FileHandler(path, fmtr)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (m muster) NetHandler(network, addr string, fmtr Format) Handler { | 
					
						
							|  |  |  | 	return must(NetHandler(network, addr, fmtr)) | 
					
						
							|  |  |  | } |