Codebase list golang-github-go-kit-kit / 328b7e6
Add syslog support (#574) * WIP: Sketch out syslogLogger based on colorLogger * WIP: test syslog writer * WIP: Add documentation and an example * WIP: Format and copy * WIP: handle edge with level key as the last and odd keyval * WIP: Shuffle code around Mingan authored 6 years ago Peter Bourgon committed 6 years ago
3 changed file(s) with 334 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 package syslog_test
1
2 import (
3 "fmt"
4
5 gosyslog "log/syslog"
6
7 "github.com/go-kit/kit/log"
8 "github.com/go-kit/kit/log/level"
9 "github.com/go-kit/kit/log/syslog"
10 )
11
12 func ExampleNewLogger_defaultPrioritySelector() {
13 // Normal syslog writer
14 w, err := gosyslog.New(gosyslog.LOG_INFO, "experiment")
15 if err != nil {
16 fmt.Println(err)
17 return
18 }
19
20 // syslog logger with logfmt formatting
21 logger := syslog.NewSyslogLogger(w, log.NewLogfmtLogger)
22 logger.Log("msg", "info because of default")
23 logger.Log(level.Key(), level.DebugValue(), "msg", "debug because of explicit level")
24 }
0 package syslog
1
2 import (
3 "bytes"
4 "io"
5 "sync"
6
7 gosyslog "log/syslog"
8
9 "github.com/go-kit/kit/log"
10 "github.com/go-kit/kit/log/level"
11 )
12
13 // SyslogWriter is an interface wrapping stdlib syslog Writer.
14 type SyslogWriter interface {
15 Write([]byte) (int, error)
16 Close() error
17 Emerg(string) error
18 Alert(string) error
19 Crit(string) error
20 Err(string) error
21 Warning(string) error
22 Notice(string) error
23 Info(string) error
24 Debug(string) error
25 }
26
27 // NewSyslogLogger returns a new Logger which writes to syslog in syslog format.
28 // The body of the log message is the formatted output from the Logger returned
29 // by newLogger.
30 func NewSyslogLogger(w SyslogWriter, newLogger func(io.Writer) log.Logger, options ...Option) log.Logger {
31 l := &syslogLogger{
32 w: w,
33 newLogger: newLogger,
34 prioritySelector: defaultPrioritySelector,
35 bufPool: sync.Pool{New: func() interface{} {
36 return &loggerBuf{}
37 }},
38 }
39
40 for _, option := range options {
41 option(l)
42 }
43
44 return l
45 }
46
47 type syslogLogger struct {
48 w SyslogWriter
49 newLogger func(io.Writer) log.Logger
50 prioritySelector PrioritySelector
51 bufPool sync.Pool
52 }
53
54 func (l *syslogLogger) Log(keyvals ...interface{}) error {
55 level := l.prioritySelector(keyvals...)
56
57 lb := l.getLoggerBuf()
58 defer l.putLoggerBuf(lb)
59 if err := lb.logger.Log(keyvals...); err != nil {
60 return err
61 }
62
63 switch level {
64 case gosyslog.LOG_EMERG:
65 return l.w.Emerg(lb.buf.String())
66 case gosyslog.LOG_ALERT:
67 return l.w.Alert(lb.buf.String())
68 case gosyslog.LOG_CRIT:
69 return l.w.Crit(lb.buf.String())
70 case gosyslog.LOG_ERR:
71 return l.w.Err(lb.buf.String())
72 case gosyslog.LOG_WARNING:
73 return l.w.Warning(lb.buf.String())
74 case gosyslog.LOG_NOTICE:
75 return l.w.Notice(lb.buf.String())
76 case gosyslog.LOG_INFO:
77 return l.w.Info(lb.buf.String())
78 case gosyslog.LOG_DEBUG:
79 return l.w.Debug(lb.buf.String())
80 default:
81 _, err := l.w.Write(lb.buf.Bytes())
82 return err
83 }
84 }
85
86 type loggerBuf struct {
87 buf *bytes.Buffer
88 logger log.Logger
89 }
90
91 func (l *syslogLogger) getLoggerBuf() *loggerBuf {
92 lb := l.bufPool.Get().(*loggerBuf)
93 if lb.buf == nil {
94 lb.buf = &bytes.Buffer{}
95 lb.logger = l.newLogger(lb.buf)
96 } else {
97 lb.buf.Reset()
98 }
99 return lb
100 }
101
102 func (l *syslogLogger) putLoggerBuf(lb *loggerBuf) {
103 l.bufPool.Put(lb)
104 }
105
106 // Option sets a parameter for syslog loggers.
107 type Option func(*syslogLogger)
108
109 // PrioritySelector inspects the list of keyvals and selects a syslog priority.
110 type PrioritySelector func(keyvals ...interface{}) gosyslog.Priority
111
112 // PrioritySelectorOption sets priority selector function to choose syslog
113 // priority.
114 func PrioritySelectorOption(selector PrioritySelector) Option {
115 return func(l *syslogLogger) { l.prioritySelector = selector }
116 }
117
118 func defaultPrioritySelector(keyvals ...interface{}) gosyslog.Priority {
119 l := len(keyvals)
120 for i := 0; i < l; i += 2 {
121 if keyvals[i] == level.Key() {
122 var val interface{}
123 if i+1 < l {
124 val = keyvals[i+1]
125 }
126 if v, ok := val.(level.Value); ok {
127 switch v {
128 case level.DebugValue():
129 return gosyslog.LOG_DEBUG
130 case level.InfoValue():
131 return gosyslog.LOG_INFO
132 case level.WarnValue():
133 return gosyslog.LOG_WARNING
134 case level.ErrorValue():
135 return gosyslog.LOG_ERR
136 }
137 }
138 }
139 }
140
141 return gosyslog.LOG_INFO
142 }
0 package syslog
1
2 import (
3 "fmt"
4 "reflect"
5 "testing"
6
7 gosyslog "log/syslog"
8
9 "github.com/go-kit/kit/log"
10 "github.com/go-kit/kit/log/level"
11 )
12
13 func TestSyslogLoggerDefaultPrioritySelector(t *testing.T) {
14 w := &testSyslogWriter{}
15 l := NewSyslogLogger(w, log.NewLogfmtLogger)
16
17 l.Log("level", level.WarnValue(), "msg", "one")
18 l.Log("level", "undefined", "msg", "two")
19 l.Log("level", level.InfoValue(), "msg", "three")
20 l.Log("level", level.ErrorValue(), "msg", "four")
21 l.Log("level", level.DebugValue(), "msg", "five")
22
23 l.Log("msg", "six", "level", level.ErrorValue())
24 l.Log("msg", "seven", "level", level.DebugValue())
25 l.Log("msg", "eight", "level", level.InfoValue())
26 l.Log("msg", "nine", "level", "undefined")
27 l.Log("msg", "ten", "level", level.WarnValue())
28
29 l.Log("level", level.ErrorValue(), "msg")
30 l.Log("msg", "eleven", "level")
31
32 want := []string{
33 "warning: level=warn msg=one\n",
34 "info: level=undefined msg=two\n",
35 "info: level=info msg=three\n",
36 "err: level=error msg=four\n",
37 "debug: level=debug msg=five\n",
38
39 "err: msg=six level=error\n",
40 "debug: msg=seven level=debug\n",
41 "info: msg=eight level=info\n",
42 "info: msg=nine level=undefined\n",
43 "warning: msg=ten level=warn\n",
44
45 "err: level=error msg=null\n",
46 "info: msg=eleven level=null\n",
47 }
48 have := w.writes
49 if !reflect.DeepEqual(want, have) {
50 t.Errorf("wrong writes: want %s, have %s", want, have)
51 }
52 }
53
54 func TestSyslogLoggerExhaustivePrioritySelector(t *testing.T) {
55 w := &testSyslogWriter{}
56 selector := func(keyvals ...interface{}) gosyslog.Priority {
57 for i := 0; i < len(keyvals); i += 2 {
58 if keyvals[i] == level.Key() {
59 if v, ok := keyvals[i+1].(string); ok {
60 switch v {
61 case "emergency":
62 return gosyslog.LOG_EMERG
63 case "alert":
64 return gosyslog.LOG_ALERT
65 case "critical":
66 return gosyslog.LOG_CRIT
67 case "error":
68 return gosyslog.LOG_ERR
69 case "warning":
70 return gosyslog.LOG_WARNING
71 case "notice":
72 return gosyslog.LOG_NOTICE
73 case "info":
74 return gosyslog.LOG_INFO
75 case "debug":
76 return gosyslog.LOG_DEBUG
77 }
78 return gosyslog.LOG_LOCAL0
79 }
80 }
81 }
82 return gosyslog.LOG_LOCAL0
83 }
84 l := NewSyslogLogger(w, log.NewLogfmtLogger, PrioritySelectorOption(selector))
85
86 l.Log("level", "warning", "msg", "one")
87 l.Log("level", "error", "msg", "two")
88 l.Log("level", "critical", "msg", "three")
89 l.Log("level", "debug", "msg", "four")
90 l.Log("level", "info", "msg", "five")
91 l.Log("level", "alert", "msg", "six")
92 l.Log("level", "emergency", "msg", "seven")
93 l.Log("level", "notice", "msg", "eight")
94 l.Log("level", "unknown", "msg", "nine")
95
96 want := []string{
97 "warning: level=warning msg=one\n",
98 "err: level=error msg=two\n",
99 "crit: level=critical msg=three\n",
100 "debug: level=debug msg=four\n",
101 "info: level=info msg=five\n",
102 "alert: level=alert msg=six\n",
103 "emerg: level=emergency msg=seven\n",
104 "notice: level=notice msg=eight\n",
105 "write: level=unknown msg=nine\n",
106 }
107 have := w.writes
108 if !reflect.DeepEqual(want, have) {
109 t.Errorf("wrong writes: want %s, have %s", want, have)
110 }
111 }
112
113 type testSyslogWriter struct {
114 writes []string
115 }
116
117 func (w *testSyslogWriter) Write(b []byte) (int, error) {
118 msg := string(b)
119 w.writes = append(w.writes, fmt.Sprintf("write: %s", msg))
120 return len(msg), nil
121 }
122
123 func (w *testSyslogWriter) Close() error {
124 return nil
125 }
126
127 func (w *testSyslogWriter) Emerg(msg string) error {
128 w.writes = append(w.writes, fmt.Sprintf("emerg: %s", msg))
129 return nil
130 }
131
132 func (w *testSyslogWriter) Alert(msg string) error {
133 w.writes = append(w.writes, fmt.Sprintf("alert: %s", msg))
134 return nil
135 }
136
137 func (w *testSyslogWriter) Crit(msg string) error {
138 w.writes = append(w.writes, fmt.Sprintf("crit: %s", msg))
139 return nil
140 }
141
142 func (w *testSyslogWriter) Err(msg string) error {
143 w.writes = append(w.writes, fmt.Sprintf("err: %s", msg))
144 return nil
145 }
146
147 func (w *testSyslogWriter) Warning(msg string) error {
148 w.writes = append(w.writes, fmt.Sprintf("warning: %s", msg))
149 return nil
150 }
151
152 func (w *testSyslogWriter) Notice(msg string) error {
153 w.writes = append(w.writes, fmt.Sprintf("notice: %s", msg))
154 return nil
155 }
156
157 func (w *testSyslogWriter) Info(msg string) error {
158 w.writes = append(w.writes, fmt.Sprintf("info: %s", msg))
159 return nil
160 }
161
162 func (w *testSyslogWriter) Debug(msg string) error {
163 w.writes = append(w.writes, fmt.Sprintf("debug: %s", msg))
164 return nil
165 }