...

Source file src/github.com/go-kit/log/stdlib.go

Documentation: github.com/go-kit/log

     1  package log
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"log"
     7  	"regexp"
     8  	"strings"
     9  )
    10  
    11  // StdlibWriter implements io.Writer by invoking the stdlib log.Print. It's
    12  // designed to be passed to a Go kit logger as the writer, for cases where
    13  // it's necessary to redirect all Go kit log output to the stdlib logger.
    14  //
    15  // If you have any choice in the matter, you shouldn't use this. Prefer to
    16  // redirect the stdlib log to the Go kit logger via NewStdlibAdapter.
    17  type StdlibWriter struct{}
    18  
    19  // Write implements io.Writer.
    20  func (w StdlibWriter) Write(p []byte) (int, error) {
    21  	log.Print(strings.TrimSpace(string(p)))
    22  	return len(p), nil
    23  }
    24  
    25  // StdlibAdapter wraps a Logger and allows it to be passed to the stdlib
    26  // logger's SetOutput. It will extract date/timestamps, filenames, and
    27  // messages, and place them under relevant keys.
    28  type StdlibAdapter struct {
    29  	Logger
    30  	timestampKey    string
    31  	fileKey         string
    32  	messageKey      string
    33  	prefix          string
    34  	joinPrefixToMsg bool
    35  }
    36  
    37  // StdlibAdapterOption sets a parameter for the StdlibAdapter.
    38  type StdlibAdapterOption func(*StdlibAdapter)
    39  
    40  // TimestampKey sets the key for the timestamp field. By default, it's "ts".
    41  func TimestampKey(key string) StdlibAdapterOption {
    42  	return func(a *StdlibAdapter) { a.timestampKey = key }
    43  }
    44  
    45  // FileKey sets the key for the file and line field. By default, it's "caller".
    46  func FileKey(key string) StdlibAdapterOption {
    47  	return func(a *StdlibAdapter) { a.fileKey = key }
    48  }
    49  
    50  // MessageKey sets the key for the actual log message. By default, it's "msg".
    51  func MessageKey(key string) StdlibAdapterOption {
    52  	return func(a *StdlibAdapter) { a.messageKey = key }
    53  }
    54  
    55  // Prefix configures the adapter to parse a prefix from stdlib log events. If
    56  // you provide a non-empty prefix to the stdlib logger, then your should provide
    57  // that same prefix to the adapter via this option.
    58  //
    59  // By default, the prefix isn't included in the msg key. Set joinPrefixToMsg to
    60  // true if you want to include the parsed prefix in the msg.
    61  func Prefix(prefix string, joinPrefixToMsg bool) StdlibAdapterOption {
    62  	return func(a *StdlibAdapter) { a.prefix = prefix; a.joinPrefixToMsg = joinPrefixToMsg }
    63  }
    64  
    65  // NewStdlibAdapter returns a new StdlibAdapter wrapper around the passed
    66  // logger. It's designed to be passed to log.SetOutput.
    67  func NewStdlibAdapter(logger Logger, options ...StdlibAdapterOption) io.Writer {
    68  	a := StdlibAdapter{
    69  		Logger:       logger,
    70  		timestampKey: "ts",
    71  		fileKey:      "caller",
    72  		messageKey:   "msg",
    73  	}
    74  	for _, option := range options {
    75  		option(&a)
    76  	}
    77  	return a
    78  }
    79  
    80  func (a StdlibAdapter) Write(p []byte) (int, error) {
    81  	p = a.handlePrefix(p)
    82  
    83  	result := subexps(p)
    84  	keyvals := []interface{}{}
    85  	var timestamp string
    86  	if date, ok := result["date"]; ok && date != "" {
    87  		timestamp = date
    88  	}
    89  	if time, ok := result["time"]; ok && time != "" {
    90  		if timestamp != "" {
    91  			timestamp += " "
    92  		}
    93  		timestamp += time
    94  	}
    95  	if timestamp != "" {
    96  		keyvals = append(keyvals, a.timestampKey, timestamp)
    97  	}
    98  	if file, ok := result["file"]; ok && file != "" {
    99  		keyvals = append(keyvals, a.fileKey, file)
   100  	}
   101  	if msg, ok := result["msg"]; ok {
   102  		msg = a.handleMessagePrefix(msg)
   103  		keyvals = append(keyvals, a.messageKey, msg)
   104  	}
   105  	if err := a.Logger.Log(keyvals...); err != nil {
   106  		return 0, err
   107  	}
   108  	return len(p), nil
   109  }
   110  
   111  func (a StdlibAdapter) handlePrefix(p []byte) []byte {
   112  	if a.prefix != "" {
   113  		p = bytes.TrimPrefix(p, []byte(a.prefix))
   114  	}
   115  	return p
   116  }
   117  
   118  func (a StdlibAdapter) handleMessagePrefix(msg string) string {
   119  	if a.prefix == "" {
   120  		return msg
   121  	}
   122  
   123  	msg = strings.TrimPrefix(msg, a.prefix)
   124  	if a.joinPrefixToMsg {
   125  		msg = a.prefix + msg
   126  	}
   127  	return msg
   128  }
   129  
   130  const (
   131  	logRegexpDate = `(?P<date>[0-9]{4}/[0-9]{2}/[0-9]{2})?[ ]?`
   132  	logRegexpTime = `(?P<time>[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?)?[ ]?`
   133  	logRegexpFile = `(?P<file>.+?:[0-9]+)?`
   134  	logRegexpMsg  = `(: )?(?P<msg>(?s:.*))`
   135  )
   136  
   137  var (
   138  	logRegexp = regexp.MustCompile(logRegexpDate + logRegexpTime + logRegexpFile + logRegexpMsg)
   139  )
   140  
   141  func subexps(line []byte) map[string]string {
   142  	m := logRegexp.FindSubmatch(line)
   143  	if len(m) < len(logRegexp.SubexpNames()) {
   144  		return map[string]string{}
   145  	}
   146  	result := map[string]string{}
   147  	for i, name := range logRegexp.SubexpNames() {
   148  		result[name] = strings.TrimRight(string(m[i]), "\n")
   149  	}
   150  	return result
   151  }
   152  

View as plain text