diff --git a/log/extended_writer.go b/log/extended_writer.go new file mode 100644 index 000000000..2703141e5 --- /dev/null +++ b/log/extended_writer.go @@ -0,0 +1,19 @@ +package log + +import "io" + +// SpecializedWriter represents an extended io.Writer class that can perform +// contextualized writes. +type SpecializedWriter interface { + GetSpecializedWriter(keyvals ...interface{}) io.Writer +} + +// specializedWriter returns a specialized writer for the specified keys or +// falls back to returning w if no specialized writer is available. +func specializedWriter(w io.Writer, keyvals ...interface{}) io.Writer { + if ew, ok := w.(SpecializedWriter); ok { + w = ew.GetSpecializedWriter(keyvals...) + } + + return w +} diff --git a/log/json_logger.go b/log/json_logger.go index 231e09955..11f88b248 100644 --- a/log/json_logger.go +++ b/log/json_logger.go @@ -31,7 +31,7 @@ func (l *jsonLogger) Log(keyvals ...interface{}) error { } merge(m, k, v) } - return json.NewEncoder(l.Writer).Encode(m) + return json.NewEncoder(specializedWriter(l.Writer, keyvals...)).Encode(m) } func merge(dst map[string]interface{}, k, v interface{}) { diff --git a/log/logfmt_logger.go b/log/logfmt_logger.go index a00305298..67f702f0b 100644 --- a/log/logfmt_logger.go +++ b/log/logfmt_logger.go @@ -55,7 +55,7 @@ func (l logfmtLogger) Log(keyvals ...interface{}) error { // The Logger interface requires implementations to be safe for concurrent // use by multiple goroutines. For this implementation that means making // only one call to l.w.Write() for each call to Log. - if _, err := l.w.Write(enc.buf.Bytes()); err != nil { + if _, err := specializedWriter(l.w, keyvals...).Write(enc.buf.Bytes()); err != nil { return err } return nil diff --git a/log/syslog/syslog.go b/log/syslog/syslog.go new file mode 100644 index 000000000..2e42a7078 --- /dev/null +++ b/log/syslog/syslog.go @@ -0,0 +1,86 @@ +// +build linux,!appengine darwin freebsd openbsd + +package syslog + +import ( + "io" + gosyslog "log/syslog" + + "github.com/go-kit/kit/log/level" +) + +type syslogWriter struct { + *gosyslog.Writer + selector func(keyvals ...interface{}) gosyslog.Priority +} + +type SyslogAdapterOption interface { + Apply(*syslogWriter) +} + +func NewSyslogWriter(w *gosyslog.Writer, options ...SyslogAdapterOption) io.Writer { + writer := &syslogWriter{ + Writer: w, + selector: defaultSyslogSelector, + } + + for _, option := range options { + option.Apply(writer) + } + + return *writer +} + +type syslogWriterAdapter struct { + f func(string) error +} + +func (a *syslogWriterAdapter) Write(b []byte) (int, error) { + return len(b), a.f(string(b)) +} + +func (w syslogWriter) GetSpecializedWriter(keyvals ...interface{}) io.Writer { + priority := w.selector(keyvals...) + + switch priority { + case gosyslog.LOG_DEBUG: + return &syslogWriterAdapter{f: w.Debug} + case gosyslog.LOG_INFO: + return &syslogWriterAdapter{f: w.Info} + case gosyslog.LOG_WARNING: + return &syslogWriterAdapter{f: w.Warning} + case gosyslog.LOG_ERR: + return &syslogWriterAdapter{f: w.Err} + } + + return w +} + +func defaultSyslogSelector(keyvals ...interface{}) gosyslog.Priority { + for i := 1; i < len(keyvals); i += 2 { + if v, ok := keyvals[i].(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 +} + +// SyslogPrioritySelector is an option that specifies the syslog priority +// selector. +type SyslogPrioritySelector struct { + PrioritySelector func(keyvals ...interface{}) gosyslog.Priority +} + +func (o SyslogPrioritySelector) Apply(w *syslogWriter) { + w.selector = o.PrioritySelector +}