Codebase list golang-github-go-kit-kit / 91461ad
Target comments by Chris Hines Tamás Gulácsi 8 years ago
5 changed file(s) with 85 addition(s) and 54 deletion(s). Raw diff Collapse all Expand all
44 "fmt"
55 "io"
66 "reflect"
7 "sync"
78 )
89
910 type jsonLogger struct {
1011 io.Writer
12 mu sync.RWMutex
1113 }
1214
1315 // NewJSONLogger returns a Logger that encodes keyvals to the Writer as a
1416 // single JSON object.
1517 func NewJSONLogger(w io.Writer) Logger {
16 return &jsonLogger{w}
18 return &jsonLogger{Writer: w}
1719 }
1820
1921 func (l *jsonLogger) Log(keyvals ...interface{}) error {
2729 }
2830 merge(m, k, v)
2931 }
30 return json.NewEncoder(l.Writer).Encode(m)
32 l.mu.RLock()
33 w := l.Writer
34 l.mu.RUnlock()
35 return json.NewEncoder(w).Encode(m)
3136 }
3237
3338 func merge(dst map[string]interface{}, k, v interface{}) {
7580 }
7681
7782 func (l *jsonLogger) Hijack(f func(io.Writer) io.Writer) {
83 l.mu.Lock()
7884 l.Writer = f(l.Writer)
85 l.mu.Unlock()
7986 }
2121 // Hijacker allows accessing the Logger's underlying io.Writer with Hijack.
2222 type Hijacker interface {
2323 // Hijack gets the Logger's underlying Writer, and replaces it with the returned one.
24 // The implementor must ensure safe concurrent calls!
2425 Hijack(func(io.Writer) io.Writer)
2526 }
2627
11
22 import (
33 "io"
4 "sync"
45
56 "gopkg.in/logfmt.v0"
67 )
78
89 type logfmtLogger struct {
9 w io.Writer
10 w io.Writer
11 mu sync.RWMutex
1012 }
1113
1214 // NewLogfmtLogger returns a logger that encodes keyvals to the Writer in
1315 // logfmt format. The passed Writer must be safe for concurrent use by
1416 // multiple goroutines if the returned Logger will be used concurrently.
1517 func NewLogfmtLogger(w io.Writer) Logger {
16 return &logfmtLogger{w}
18 return &logfmtLogger{w: w}
1719 }
1820
1921 func (l logfmtLogger) Log(keyvals ...interface{}) error {
2628 return err
2729 }
2830 b = append(b, '\n')
29 if _, err := l.w.Write(b); err != nil {
31 l.mu.RLock()
32 w := l.w
33 l.mu.RUnlock()
34 if _, err := w.Write(b); err != nil {
3035 return err
3136 }
3237 return nil
3338 }
3439
3540 func (l *logfmtLogger) Hijack(f func(io.Writer) io.Writer) {
41 l.mu.Lock()
3642 l.w = f(l.w)
43 l.mu.Unlock()
3744 }
3030 return c.Fg == NoColor && c.Bg == NoColor
3131 }
3232
33 type ColorOption struct {
34 Key FgBgColor
35 Value func(interface{}) FgBgColor
36 }
37
3833 var _ = log.Logger((*colorLogger)(nil))
3934
40 // NewColorLogger returns a log.Logger which prouces nice colored logs.
35 // NewColorLogger returns a log.Logger which produces nice colored logs.
4136 // It colors whole records based on the FgBgColor returned by the color function.
4237 //
4338 // For example for such a function, see LevelColor.
6055 // logger.Log("c", "c is uncolored value", "err", nil)
6156 // logger.Log("c", "c is colored 'cause err colors it", "err", errors.New("coloring error"))
6257 func NewColorLogger(logger log.Logger, color func(keyvals ...interface{}) FgBgColor) log.Logger {
58 // FIXME(tgulacsi): I don't understand why can't I use log.Hijacker here...
59 hj, ok := logger.(interface {
60 Hijack(func(io.Writer) io.Writer)
61 })
62 if !ok {
63 return logger
64 }
6365 cl := &colorLogger{
6466 logger: logger,
6567 }
66 if hj, ok := logger.(log.Hijacker); ok {
67 cl.color = color // otherwise, no coloring is possible!
68 hj.Hijack(func(w io.Writer) io.Writer {
69 cl.w = w
70 return cl
71 })
72 }
68 cl.color = color // otherwise, no coloring is possible!
69 hj.Hijack(func(w io.Writer) io.Writer {
70 cl.w = w
71 return cl
72 })
7373 return cl
7474 }
7575
7777 logger log.Logger
7878 color func(keyvals ...interface{}) FgBgColor
7979 w io.Writer
80 mu sync.RWMutex // protects w
8081
8182 actColor FgBgColor
8283 actColorMu sync.RWMutex // protects actColor
118119 return n + m, err
119120 }
120121 n += m
121 m, err = l.w.Write([]byte("\x1b[0m"))
122 l.mu.RLock()
123 w := l.w
124 l.mu.RUnlock()
125 m, err = w.Write([]byte("\x1b[0m"))
122126 return n + m, err
123127 }
124128
125129 func (l *colorLogger) Hijack(f func(io.Writer) io.Writer) {
130 l.mu.Lock()
126131 l.w = f(l.w)
132 l.mu.Unlock()
127133 }
128134
129 type ColoredValue struct {
130 FgBgColor
131 Value interface{}
132 }
133
134 func (cv ColoredValue) String() string {
135 // http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
136 if cv.Fg == 0 && cv.Bg == 0 {
137 return fmt.Sprintf("%v", cv.Value)
138 }
139 if cv.Bg == 0 {
140 return fmt.Sprintf("\x1b[%dm%v\x1b[0m", 30+cv.Fg, cv.Value)
141 }
142 if cv.Fg == 0 {
143 return fmt.Sprintf("\x1b[%dm%v\x1b[0m", 40+cv.Bg, cv.Value)
144 }
145 return fmt.Sprintf("\x1b[%dm\x1b[%dm%v\x1b[0m", 30+cv.Fg, 40+cv.Bg, cv.Value)
146 }
147
148 func AsString(v interface{}) string {
135 func asString(v interface{}) string {
149136 switch x := v.(type) {
150137 case string:
151138 return x
157144 return fmt.Sprintf("%v", x)
158145 }
159146 }
147
148 // LevelColor returns the color for the record based on the value of the "level" key, if exists.
149 func LevelColor(keyvals ...interface{}) FgBgColor {
150 for i := 0; i < len(keyvals); i += 2 {
151 if asString(keyvals[i]) != "level" {
152 continue
153 }
154 switch asString(keyvals[i+1]) {
155 case "debug":
156 return FgBgColor{Fg: Green}
157 case "info":
158 return FgBgColor{Fg: White}
159 case "warn":
160 return FgBgColor{Fg: Yellow}
161 case "error":
162 return FgBgColor{Fg: Red}
163 case "crit":
164 return FgBgColor{Fg: Default, Bg: Red}
165 default:
166 return FgBgColor{}
167 }
168 }
169 return FgBgColor{}
170 }
22 import (
33 "bytes"
44 "errors"
5 "fmt"
56 "io"
67 "io/ioutil"
78 "strconv"
1011
1112 "github.com/go-kit/kit/log"
1213 "github.com/go-kit/kit/log/term"
13 "gopkg.in/logfmt.v0"
1414 )
1515
1616 type mymap map[int]int
1919
2020 func TestColorLogger(t *testing.T) {
2121 var buf bytes.Buffer
22 logger := newColorLogger(t, &buf)
22 logger := newColorLogger(&buf)
2323
2424 if err := logger.Log("hello", "world"); err != nil {
2525 t.Fatal(err)
3535 if want, have := "\u001b[32m\u001b[48ma=1 err=error\n\u001b[0m", buf.String(); want != have {
3636 t.Errorf("want %#v, have %#v", want, have)
3737 }
38
39 buf.Reset()
40 if err := logger.Log("std_map", map[int]int{1: 2}, "my_map", mymap{0: 0}); err != nil {
41 t.Fatal(err)
42 }
43 if want, have := "std_map=\""+logfmt.ErrUnsupportedValueType.Error()+"\" my_map=special_behavior\n", buf.String(); want != have {
44 t.Errorf("want %#v, have %#v", want, have)
45 }
4638 }
4739
48 func newColorLogger(t testing.TB, w io.Writer) log.Logger {
40 func newColorLogger(w io.Writer) log.Logger {
4941 return term.NewColorLogger(log.NewLogfmtLogger(w),
5042 func(keyvals ...interface{}) term.FgBgColor {
5143 for i := 0; i < len(keyvals); i += 2 {
52 key := term.AsString(keyvals[i])
44 key := asString(keyvals[i])
5345 if key == "a" {
5446 return term.FgBgColor{Fg: term.Green, Bg: term.Default}
5547 }
6254 }
6355
6456 func BenchmarkColorLoggerSimple(b *testing.B) {
65 benchmarkRunner(b, newColorLogger(b, ioutil.Discard), baseMessage)
57 benchmarkRunner(b, newColorLogger(ioutil.Discard), baseMessage)
6658 }
6759
6860 func BenchmarkColorLoggerContextual(b *testing.B) {
69 benchmarkRunner(b, newColorLogger(b, ioutil.Discard), withMessage)
61 benchmarkRunner(b, newColorLogger(ioutil.Discard), withMessage)
7062 }
7163
7264 func TestColorLoggerConcurrency(t *testing.T) {
73 testConcurrency(t, newColorLogger(t, ioutil.Discard))
65 testConcurrency(t, newColorLogger(ioutil.Discard))
7466 }
7567
7668 // copied from log/benchmark_test.go
10597 logger.Log("key", strconv.FormatInt(int64(i), 10))
10698 }
10799 }
100
101 func asString(v interface{}) string {
102 switch x := v.(type) {
103 case string:
104 return x
105 case fmt.Stringer:
106 return x.String()
107 case fmt.Formatter:
108 return fmt.Sprint(x)
109 default:
110 return fmt.Sprintf("%v", x)
111 }
112 }