From 736a1cea482e3dc223c20abce9d86e1bdd75216f Mon Sep 17 00:00:00 2001 From: Masahiro Sano Date: Sat, 13 Feb 2016 20:42:11 +0900 Subject: [PATCH] add LTSV logger --- log/ltsv_logger.go | 80 +++++++++++++++++++++++++++++++++++++++++ log/ltsv_logger_test.go | 78 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 log/ltsv_logger.go create mode 100644 log/ltsv_logger_test.go diff --git a/log/ltsv_logger.go b/log/ltsv_logger.go new file mode 100644 index 000000000..803d65bd7 --- /dev/null +++ b/log/ltsv_logger.go @@ -0,0 +1,80 @@ +package log + +import ( + "fmt" + "github.com/najeira/ltsv" + "io" + "reflect" +) + +type ltsvLogger struct { + w io.Writer +} + +// NewLTSVLogger returns a Logger that encodes keyvals to the Writer as +// Labeled Tab-separated Values format. +func NewLTSVLogger(w io.Writer) Logger { + return <svLogger{ + w: w, + } +} + +func (l *ltsvLogger) Log(keyvals ...interface{}) error { + n := (len(keyvals) + 1) / 2 // +1 to handle case when len is odd + m := make(map[string]string, n) + for i := 0; i < len(keyvals); i += 2 { + k := keyvals[i] + var v interface{} = ErrMissingValue + if i+1 < len(keyvals) { + v = keyvals[i+1] + } + mergeStringMap(m, k, v) + } + + w := ltsv.NewWriter(l.w) + if err := w.Write(m); err != nil { + return err + } + w.Flush() + return nil +} + +func mergeStringMap(dst map[string]string, k, v interface{}) { + var key string + switch x := k.(type) { + case string: + key = x + case fmt.Stringer: + key = safeString(x) + default: + key = fmt.Sprint(x) + } + + var val string + switch x := v.(type) { + case string: + val = x + case error: + val = safeErrorString(x) + case fmt.Stringer: + val = safeString(x) + default: + val = fmt.Sprint(x) + } + + dst[key] = val +} + +func safeErrorString(err error) (s string) { + defer func() { + if panicVal := recover(); panicVal != nil { + if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() { + s = "" + } else { + panic(panicVal) + } + } + }() + s = err.Error() + return +} diff --git a/log/ltsv_logger_test.go b/log/ltsv_logger_test.go new file mode 100644 index 000000000..69c914523 --- /dev/null +++ b/log/ltsv_logger_test.go @@ -0,0 +1,78 @@ +package log_test + +import ( + "bytes" + "errors" + "io/ioutil" + "sort" + "strings" + "testing" + + "github.com/go-kit/kit/log" +) + +func TestLTSVLogger(t *testing.T) { + t.Parallel() + buf := &bytes.Buffer{} + logger := log.NewLTSVLogger(buf) + if err := logger.Log("err", errors.New("err"), "m", map[string]int{"0": 0}, "a", []int{1, 2, 3}); err != nil { + t.Fatal(err) + } + + if want, have := "\n", buf.String(); !strings.HasSuffix(have, want) { + t.Errorf("must end with %q, got %#v", want, have) + } + + values := strings.Split(strings.TrimSpace(buf.String()), "\t") + sort.Strings(values) + + if want, have := "a:[1 2 3]", values[0]; want != have { + t.Errorf("\nwant %#v\nhave %#v", want, have) + } + + if want, have := "err:err", values[1]; want != have { + t.Errorf("\nwant %#v\nhave %#v", want, have) + } + + if want, have := "m:map[0:0]", values[2]; want != have { + t.Errorf("\nwant %#v\nhave %#v", want, have) + } +} + +func TestLTSVLoggerNilStringerKey(t *testing.T) { + t.Parallel() + + buf := &bytes.Buffer{} + logger := log.NewLTSVLogger(buf) + if err := logger.Log((*stringer)(nil), "v"); err != nil { + t.Fatal(err) + } + if want, have := "NULL:v\n", buf.String(); want != have { + t.Errorf("\nwant %#v\nhave %#v", want, have) + } +} + +func TestLTSVLoggerNilErrorValue(t *testing.T) { + t.Parallel() + + buf := &bytes.Buffer{} + logger := log.NewLTSVLogger(buf) + if err := logger.Log("err", (*stringError)(nil)); err != nil { + t.Fatal(err) + } + if want, have := "err:\n", buf.String(); want != have { + t.Errorf("\nwant %#v\nhave %#v", want, have) + } +} + +func BenchmarkLTSVLoggerSimple(b *testing.B) { + benchmarkRunner(b, log.NewLTSVLogger(ioutil.Discard), baseMessage) +} + +func BenchmarkLTSVLoggerContextual(b *testing.B) { + benchmarkRunner(b, log.NewLTSVLogger(ioutil.Discard), withMessage) +} + +func TestLTSVLoggerConcurrency(t *testing.T) { + testConcurrency(t, log.NewLTSVLogger(ioutil.Discard)) +}