-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 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
- Loading branch information
1 parent
51bc750
commit 328b7e6
Showing
3 changed files
with
334 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package syslog_test | ||
|
||
import ( | ||
"fmt" | ||
|
||
gosyslog "log/syslog" | ||
|
||
"github.com/go-kit/kit/log" | ||
"github.com/go-kit/kit/log/level" | ||
"github.com/go-kit/kit/log/syslog" | ||
) | ||
|
||
func ExampleNewLogger_defaultPrioritySelector() { | ||
// Normal syslog writer | ||
w, err := gosyslog.New(gosyslog.LOG_INFO, "experiment") | ||
if err != nil { | ||
fmt.Println(err) | ||
return | ||
} | ||
|
||
// syslog logger with logfmt formatting | ||
logger := syslog.NewSyslogLogger(w, log.NewLogfmtLogger) | ||
logger.Log("msg", "info because of default") | ||
logger.Log(level.Key(), level.DebugValue(), "msg", "debug because of explicit level") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
package syslog | ||
|
||
import ( | ||
"bytes" | ||
"io" | ||
"sync" | ||
|
||
gosyslog "log/syslog" | ||
|
||
"github.com/go-kit/kit/log" | ||
"github.com/go-kit/kit/log/level" | ||
) | ||
|
||
// SyslogWriter is an interface wrapping stdlib syslog Writer. | ||
type SyslogWriter interface { | ||
Write([]byte) (int, error) | ||
Close() error | ||
Emerg(string) error | ||
Alert(string) error | ||
Crit(string) error | ||
Err(string) error | ||
Warning(string) error | ||
Notice(string) error | ||
Info(string) error | ||
Debug(string) error | ||
} | ||
|
||
// NewSyslogLogger returns a new Logger which writes to syslog in syslog format. | ||
// The body of the log message is the formatted output from the Logger returned | ||
// by newLogger. | ||
func NewSyslogLogger(w SyslogWriter, newLogger func(io.Writer) log.Logger, options ...Option) log.Logger { | ||
l := &syslogLogger{ | ||
w: w, | ||
newLogger: newLogger, | ||
prioritySelector: defaultPrioritySelector, | ||
bufPool: sync.Pool{New: func() interface{} { | ||
return &loggerBuf{} | ||
}}, | ||
} | ||
|
||
for _, option := range options { | ||
option(l) | ||
} | ||
|
||
return l | ||
} | ||
|
||
type syslogLogger struct { | ||
w SyslogWriter | ||
newLogger func(io.Writer) log.Logger | ||
prioritySelector PrioritySelector | ||
bufPool sync.Pool | ||
} | ||
|
||
func (l *syslogLogger) Log(keyvals ...interface{}) error { | ||
level := l.prioritySelector(keyvals...) | ||
|
||
lb := l.getLoggerBuf() | ||
defer l.putLoggerBuf(lb) | ||
if err := lb.logger.Log(keyvals...); err != nil { | ||
return err | ||
} | ||
|
||
switch level { | ||
case gosyslog.LOG_EMERG: | ||
return l.w.Emerg(lb.buf.String()) | ||
case gosyslog.LOG_ALERT: | ||
return l.w.Alert(lb.buf.String()) | ||
case gosyslog.LOG_CRIT: | ||
return l.w.Crit(lb.buf.String()) | ||
case gosyslog.LOG_ERR: | ||
return l.w.Err(lb.buf.String()) | ||
case gosyslog.LOG_WARNING: | ||
return l.w.Warning(lb.buf.String()) | ||
case gosyslog.LOG_NOTICE: | ||
return l.w.Notice(lb.buf.String()) | ||
case gosyslog.LOG_INFO: | ||
return l.w.Info(lb.buf.String()) | ||
case gosyslog.LOG_DEBUG: | ||
return l.w.Debug(lb.buf.String()) | ||
default: | ||
_, err := l.w.Write(lb.buf.Bytes()) | ||
return err | ||
} | ||
} | ||
|
||
type loggerBuf struct { | ||
buf *bytes.Buffer | ||
logger log.Logger | ||
} | ||
|
||
func (l *syslogLogger) getLoggerBuf() *loggerBuf { | ||
lb := l.bufPool.Get().(*loggerBuf) | ||
if lb.buf == nil { | ||
lb.buf = &bytes.Buffer{} | ||
lb.logger = l.newLogger(lb.buf) | ||
} else { | ||
lb.buf.Reset() | ||
} | ||
return lb | ||
} | ||
|
||
func (l *syslogLogger) putLoggerBuf(lb *loggerBuf) { | ||
l.bufPool.Put(lb) | ||
} | ||
|
||
// Option sets a parameter for syslog loggers. | ||
type Option func(*syslogLogger) | ||
|
||
// PrioritySelector inspects the list of keyvals and selects a syslog priority. | ||
type PrioritySelector func(keyvals ...interface{}) gosyslog.Priority | ||
|
||
// PrioritySelectorOption sets priority selector function to choose syslog | ||
// priority. | ||
func PrioritySelectorOption(selector PrioritySelector) Option { | ||
return func(l *syslogLogger) { l.prioritySelector = selector } | ||
} | ||
|
||
func defaultPrioritySelector(keyvals ...interface{}) gosyslog.Priority { | ||
l := len(keyvals) | ||
for i := 0; i < l; i += 2 { | ||
if keyvals[i] == level.Key() { | ||
var val interface{} | ||
if i+1 < l { | ||
val = keyvals[i+1] | ||
} | ||
if v, ok := val.(level.Value); ok { | ||
switch v { | ||
case level.DebugValue(): | ||
return gosyslog.LOG_DEBUG | ||
case level.InfoValue(): | ||
return gosyslog.LOG_INFO | ||
case level.WarnValue(): | ||
return gosyslog.LOG_WARNING | ||
case level.ErrorValue(): | ||
return gosyslog.LOG_ERR | ||
} | ||
} | ||
} | ||
} | ||
|
||
return gosyslog.LOG_INFO | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
package syslog | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
"testing" | ||
|
||
gosyslog "log/syslog" | ||
|
||
"github.com/go-kit/kit/log" | ||
"github.com/go-kit/kit/log/level" | ||
) | ||
|
||
func TestSyslogLoggerDefaultPrioritySelector(t *testing.T) { | ||
w := &testSyslogWriter{} | ||
l := NewSyslogLogger(w, log.NewLogfmtLogger) | ||
|
||
l.Log("level", level.WarnValue(), "msg", "one") | ||
l.Log("level", "undefined", "msg", "two") | ||
l.Log("level", level.InfoValue(), "msg", "three") | ||
l.Log("level", level.ErrorValue(), "msg", "four") | ||
l.Log("level", level.DebugValue(), "msg", "five") | ||
|
||
l.Log("msg", "six", "level", level.ErrorValue()) | ||
l.Log("msg", "seven", "level", level.DebugValue()) | ||
l.Log("msg", "eight", "level", level.InfoValue()) | ||
l.Log("msg", "nine", "level", "undefined") | ||
l.Log("msg", "ten", "level", level.WarnValue()) | ||
|
||
l.Log("level", level.ErrorValue(), "msg") | ||
l.Log("msg", "eleven", "level") | ||
|
||
want := []string{ | ||
"warning: level=warn msg=one\n", | ||
"info: level=undefined msg=two\n", | ||
"info: level=info msg=three\n", | ||
"err: level=error msg=four\n", | ||
"debug: level=debug msg=five\n", | ||
|
||
"err: msg=six level=error\n", | ||
"debug: msg=seven level=debug\n", | ||
"info: msg=eight level=info\n", | ||
"info: msg=nine level=undefined\n", | ||
"warning: msg=ten level=warn\n", | ||
|
||
"err: level=error msg=null\n", | ||
"info: msg=eleven level=null\n", | ||
} | ||
have := w.writes | ||
if !reflect.DeepEqual(want, have) { | ||
t.Errorf("wrong writes: want %s, have %s", want, have) | ||
} | ||
} | ||
|
||
func TestSyslogLoggerExhaustivePrioritySelector(t *testing.T) { | ||
w := &testSyslogWriter{} | ||
selector := func(keyvals ...interface{}) gosyslog.Priority { | ||
for i := 0; i < len(keyvals); i += 2 { | ||
if keyvals[i] == level.Key() { | ||
if v, ok := keyvals[i+1].(string); ok { | ||
switch v { | ||
case "emergency": | ||
return gosyslog.LOG_EMERG | ||
case "alert": | ||
return gosyslog.LOG_ALERT | ||
case "critical": | ||
return gosyslog.LOG_CRIT | ||
case "error": | ||
return gosyslog.LOG_ERR | ||
case "warning": | ||
return gosyslog.LOG_WARNING | ||
case "notice": | ||
return gosyslog.LOG_NOTICE | ||
case "info": | ||
return gosyslog.LOG_INFO | ||
case "debug": | ||
return gosyslog.LOG_DEBUG | ||
} | ||
return gosyslog.LOG_LOCAL0 | ||
} | ||
} | ||
} | ||
return gosyslog.LOG_LOCAL0 | ||
} | ||
l := NewSyslogLogger(w, log.NewLogfmtLogger, PrioritySelectorOption(selector)) | ||
|
||
l.Log("level", "warning", "msg", "one") | ||
l.Log("level", "error", "msg", "two") | ||
l.Log("level", "critical", "msg", "three") | ||
l.Log("level", "debug", "msg", "four") | ||
l.Log("level", "info", "msg", "five") | ||
l.Log("level", "alert", "msg", "six") | ||
l.Log("level", "emergency", "msg", "seven") | ||
l.Log("level", "notice", "msg", "eight") | ||
l.Log("level", "unknown", "msg", "nine") | ||
|
||
want := []string{ | ||
"warning: level=warning msg=one\n", | ||
"err: level=error msg=two\n", | ||
"crit: level=critical msg=three\n", | ||
"debug: level=debug msg=four\n", | ||
"info: level=info msg=five\n", | ||
"alert: level=alert msg=six\n", | ||
"emerg: level=emergency msg=seven\n", | ||
"notice: level=notice msg=eight\n", | ||
"write: level=unknown msg=nine\n", | ||
} | ||
have := w.writes | ||
if !reflect.DeepEqual(want, have) { | ||
t.Errorf("wrong writes: want %s, have %s", want, have) | ||
} | ||
} | ||
|
||
type testSyslogWriter struct { | ||
writes []string | ||
} | ||
|
||
func (w *testSyslogWriter) Write(b []byte) (int, error) { | ||
msg := string(b) | ||
w.writes = append(w.writes, fmt.Sprintf("write: %s", msg)) | ||
return len(msg), nil | ||
} | ||
|
||
func (w *testSyslogWriter) Close() error { | ||
return nil | ||
} | ||
|
||
func (w *testSyslogWriter) Emerg(msg string) error { | ||
w.writes = append(w.writes, fmt.Sprintf("emerg: %s", msg)) | ||
return nil | ||
} | ||
|
||
func (w *testSyslogWriter) Alert(msg string) error { | ||
w.writes = append(w.writes, fmt.Sprintf("alert: %s", msg)) | ||
return nil | ||
} | ||
|
||
func (w *testSyslogWriter) Crit(msg string) error { | ||
w.writes = append(w.writes, fmt.Sprintf("crit: %s", msg)) | ||
return nil | ||
} | ||
|
||
func (w *testSyslogWriter) Err(msg string) error { | ||
w.writes = append(w.writes, fmt.Sprintf("err: %s", msg)) | ||
return nil | ||
} | ||
|
||
func (w *testSyslogWriter) Warning(msg string) error { | ||
w.writes = append(w.writes, fmt.Sprintf("warning: %s", msg)) | ||
return nil | ||
} | ||
|
||
func (w *testSyslogWriter) Notice(msg string) error { | ||
w.writes = append(w.writes, fmt.Sprintf("notice: %s", msg)) | ||
return nil | ||
} | ||
|
||
func (w *testSyslogWriter) Info(msg string) error { | ||
w.writes = append(w.writes, fmt.Sprintf("info: %s", msg)) | ||
return nil | ||
} | ||
|
||
func (w *testSyslogWriter) Debug(msg string) error { | ||
w.writes = append(w.writes, fmt.Sprintf("debug: %s", msg)) | ||
return nil | ||
} |