1package log
2
3import (
4	"bytes"
5	"io"
6	"sync"
7
8	"github.com/go-logfmt/logfmt"
9)
10
11type logfmtEncoder struct {
12	*logfmt.Encoder
13	buf bytes.Buffer
14}
15
16func (l *logfmtEncoder) Reset() {
17	l.Encoder.Reset()
18	l.buf.Reset()
19}
20
21var logfmtEncoderPool = sync.Pool{
22	New: func() interface{} {
23		var enc logfmtEncoder
24		enc.Encoder = logfmt.NewEncoder(&enc.buf)
25		return &enc
26	},
27}
28
29type logfmtLogger struct {
30	w io.Writer
31}
32
33// NewLogfmtLogger returns a logger that encodes keyvals to the Writer in
34// logfmt format. Each log event produces no more than one call to w.Write.
35// The passed Writer must be safe for concurrent use by multiple goroutines if
36// the returned Logger will be used concurrently.
37func NewLogfmtLogger(w io.Writer) Logger {
38	return &logfmtLogger{w}
39}
40
41func (l logfmtLogger) Log(keyvals ...interface{}) error {
42	enc := logfmtEncoderPool.Get().(*logfmtEncoder)
43	enc.Reset()
44	defer logfmtEncoderPool.Put(enc)
45
46	if err := enc.EncodeKeyvals(keyvals...); err != nil {
47		return err
48	}
49
50	// Add newline to the end of the buffer
51	if err := enc.EndRecord(); err != nil {
52		return err
53	}
54
55	// The Logger interface requires implementations to be safe for concurrent
56	// use by multiple goroutines. For this implementation that means making
57	// only one call to l.w.Write() for each call to Log.
58	if _, err := l.w.Write(enc.buf.Bytes()); err != nil {
59		return err
60	}
61	return nil
62}
63