123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- package log
- import (
- "fmt"
- "io"
- "net"
- "os"
- "reflect"
- "sync"
- "github.com/go-stack/stack"
- )
- // Handler defines where and how log records are written.
- // 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
- }
- // 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 {
- r.Ctx = append(r.Ctx, "fn", formatCall("%+n", r.Call))
- return h.Log(r)
- })
- }
- // 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)
- }
- // 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)
- }
- // MultiHandler dispatches any write to each of its handlers.
- // 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
- })
- }
- // FailoverHandler writes all log records to the first handler
- // 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(
- // log.Must.NetHandler("tcp", ":9090", log.JSONFormat()),
- // 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
- }
- r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err)
- }
- 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
- }
- values := make([]interface{}, len(results))
- for i, v := range results {
- values[i] = v.Interface()
- }
- return values, nil
- }
- // 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
- })
- }
- // Must provides the following Handler creation functions
- // 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))
- }
|