From 95f5e26b9f6e3d1a733315dba1dec2893ec7eef5 Mon Sep 17 00:00:00 2001 From: Julien Kauffmann Date: Mon, 6 Mar 2017 23:49:11 -0500 Subject: [PATCH 1/3] Added basic syslog support --- log/level/syslog.go | 49 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 log/level/syslog.go diff --git a/log/level/syslog.go b/log/level/syslog.go new file mode 100644 index 000000000..1c4b853a0 --- /dev/null +++ b/log/level/syslog.go @@ -0,0 +1,49 @@ +package level + +import ( + "io" + "log/syslog" + + "github.com/go-kit/kit/log" +) + +type syslogWriterAdapter struct { + write func(string) error +} + +func (w syslogWriterAdapter) Write(b []byte) (int, error) { + return len(b), w.write(string(b)) +} + +// NewSyslogLogger returns a Logger which writes to syslog. +func NewSyslogLogger(w *syslog.Writer, newLogger func(io.Writer) log.Logger) log.Logger { + logger := &syslogLogger{ + w: w, + loggers: make(map[Value]log.Logger), + } + + logger.loggers[debugValue] = newLogger(syslogWriterAdapter{write: w.Debug}) + logger.loggers[infoValue] = newLogger(syslogWriterAdapter{write: w.Info}) + logger.loggers[warnValue] = newLogger(syslogWriterAdapter{write: w.Warning}) + logger.loggers[errorValue] = newLogger(syslogWriterAdapter{write: w.Err}) + + return logger +} + +type syslogLogger struct { + w *syslog.Writer + loggers map[Value]log.Logger +} + +func (l *syslogLogger) Log(keyvals ...interface{}) error { + level := infoValue + + for i := 1; i < len(keyvals); i += 2 { + if v, ok := keyvals[i].(*levelValue); ok { + level = v + break + } + } + + return l.loggers[level].Log(keyvals...) +} From e9365255b3208117d3fcd1a96568c3d8fbc2ae49 Mon Sep 17 00:00:00 2001 From: Julien Kauffmann Date: Tue, 7 Mar 2017 08:52:28 -0500 Subject: [PATCH 2/3] POC --- log/extended_writer.go | 19 ++++++++++ log/json_logger.go | 2 +- log/level/syslog.go | 49 ------------------------ log/logfmt_logger.go | 2 +- log/syslog.go | 86 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 107 insertions(+), 51 deletions(-) create mode 100644 log/extended_writer.go delete mode 100644 log/level/syslog.go create mode 100644 log/syslog.go diff --git a/log/extended_writer.go b/log/extended_writer.go new file mode 100644 index 000000000..152e42463 --- /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..358e06b36 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)).Encode(m) } func merge(dst map[string]interface{}, k, v interface{}) { diff --git a/log/level/syslog.go b/log/level/syslog.go deleted file mode 100644 index 1c4b853a0..000000000 --- a/log/level/syslog.go +++ /dev/null @@ -1,49 +0,0 @@ -package level - -import ( - "io" - "log/syslog" - - "github.com/go-kit/kit/log" -) - -type syslogWriterAdapter struct { - write func(string) error -} - -func (w syslogWriterAdapter) Write(b []byte) (int, error) { - return len(b), w.write(string(b)) -} - -// NewSyslogLogger returns a Logger which writes to syslog. -func NewSyslogLogger(w *syslog.Writer, newLogger func(io.Writer) log.Logger) log.Logger { - logger := &syslogLogger{ - w: w, - loggers: make(map[Value]log.Logger), - } - - logger.loggers[debugValue] = newLogger(syslogWriterAdapter{write: w.Debug}) - logger.loggers[infoValue] = newLogger(syslogWriterAdapter{write: w.Info}) - logger.loggers[warnValue] = newLogger(syslogWriterAdapter{write: w.Warning}) - logger.loggers[errorValue] = newLogger(syslogWriterAdapter{write: w.Err}) - - return logger -} - -type syslogLogger struct { - w *syslog.Writer - loggers map[Value]log.Logger -} - -func (l *syslogLogger) Log(keyvals ...interface{}) error { - level := infoValue - - for i := 1; i < len(keyvals); i += 2 { - if v, ok := keyvals[i].(*levelValue); ok { - level = v - break - } - } - - return l.loggers[level].Log(keyvals...) -} diff --git a/log/logfmt_logger.go b/log/logfmt_logger.go index a00305298..b34446505 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).Write(enc.buf.Bytes()); err != nil { return err } return nil diff --git a/log/syslog.go b/log/syslog.go new file mode 100644 index 000000000..fe31719da --- /dev/null +++ b/log/syslog.go @@ -0,0 +1,86 @@ +// +build linux,!appengine darwin freebsd openbsd + +package log + +import ( + "io" + "log/syslog" + + "github.com/go-kit/kit/log/level" +) + +type syslogWriter struct { + *syslog.Writer + selector func(keyvals ...interface{}) syslog.Priority +} + +type SyslogAdapterOption interface { + Apply(*syslogWriter) +} + +func NewSyslogWriter(w *syslog.Writer, options ...SyslogAdapterOption) io.Writer { + writer := &syslogWriter{ + syslog.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 syslog.LOG_DEBUG: + return syslogWriterAdapter{f: w.Debug} + case syslog.LOG_INFO: + return syslogWriterAdapter{f: w.Info} + case syslog.LOG_WARN: + return syslogWriterAdapter{f: w.Warn} + case syslog.LOG_ERR: + return syslogWriterAdapter{f: w.Error} + } + + return w +} + +func defaultSyslogSelector(keyvals ...interface{}) syslog.Priority { + for i := 1; i < len(keyvals); i += 2 { + if v, ok := keyvals[i].(*Value); ok { + switch v { + case level.DebugValue(): + return syslog.LOG_DEBUG + case level.InfoValue(): + return syslog.LOG_INFO + case level.WarnValue(): + return syslog.LOG_WARN + case level.ErrorValue(): + return syslog.LOG_ERR + } + } + } + + return syslog.LOG_INFO +} + +// SyslogPrioritySelector is an option that specifies the syslog priority +// selector. +type SyslogPrioritySelector struct { + PrioritySelector func(keyvals ...interface{}) syslog.Priority +} + +func (o SyslogPrioritySelector) Apply(w *syslogWriter) { + w.selector = o.PrioritySelector +} From eca151a05228cf7929d9a3b76c49ff0df468c452 Mon Sep 17 00:00:00 2001 From: Julien Kauffmann Date: Tue, 7 Mar 2017 17:51:02 -0500 Subject: [PATCH 3/3] Fixed build --- log/extended_writer.go | 2 +- log/json_logger.go | 2 +- log/logfmt_logger.go | 2 +- log/{ => syslog}/syslog.go | 46 +++++++++++++++++++------------------- 4 files changed, 26 insertions(+), 26 deletions(-) rename log/{ => syslog}/syslog.go (54%) diff --git a/log/extended_writer.go b/log/extended_writer.go index 152e42463..2703141e5 100644 --- a/log/extended_writer.go +++ b/log/extended_writer.go @@ -12,7 +12,7 @@ type SpecializedWriter interface { // 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) + w = ew.GetSpecializedWriter(keyvals...) } return w diff --git a/log/json_logger.go b/log/json_logger.go index 358e06b36..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(specializedWriter(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 b34446505..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 := specializedWriter(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.go b/log/syslog/syslog.go similarity index 54% rename from log/syslog.go rename to log/syslog/syslog.go index fe31719da..2e42a7078 100644 --- a/log/syslog.go +++ b/log/syslog/syslog.go @@ -1,27 +1,27 @@ // +build linux,!appengine darwin freebsd openbsd -package log +package syslog import ( "io" - "log/syslog" + gosyslog "log/syslog" "github.com/go-kit/kit/log/level" ) type syslogWriter struct { - *syslog.Writer - selector func(keyvals ...interface{}) syslog.Priority + *gosyslog.Writer + selector func(keyvals ...interface{}) gosyslog.Priority } type SyslogAdapterOption interface { Apply(*syslogWriter) } -func NewSyslogWriter(w *syslog.Writer, options ...SyslogAdapterOption) io.Writer { +func NewSyslogWriter(w *gosyslog.Writer, options ...SyslogAdapterOption) io.Writer { writer := &syslogWriter{ - syslog.Writer: w, - selector: defaultSyslogSelector, + Writer: w, + selector: defaultSyslogSelector, } for _, option := range options { @@ -43,42 +43,42 @@ func (w syslogWriter) GetSpecializedWriter(keyvals ...interface{}) io.Writer { priority := w.selector(keyvals...) switch priority { - case syslog.LOG_DEBUG: - return syslogWriterAdapter{f: w.Debug} - case syslog.LOG_INFO: - return syslogWriterAdapter{f: w.Info} - case syslog.LOG_WARN: - return syslogWriterAdapter{f: w.Warn} - case syslog.LOG_ERR: - return syslogWriterAdapter{f: w.Error} + 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{}) syslog.Priority { +func defaultSyslogSelector(keyvals ...interface{}) gosyslog.Priority { for i := 1; i < len(keyvals); i += 2 { - if v, ok := keyvals[i].(*Value); ok { + if v, ok := keyvals[i].(level.Value); ok { switch v { case level.DebugValue(): - return syslog.LOG_DEBUG + return gosyslog.LOG_DEBUG case level.InfoValue(): - return syslog.LOG_INFO + return gosyslog.LOG_INFO case level.WarnValue(): - return syslog.LOG_WARN + return gosyslog.LOG_WARNING case level.ErrorValue(): - return syslog.LOG_ERR + return gosyslog.LOG_ERR } } } - return syslog.LOG_INFO + return gosyslog.LOG_INFO } // SyslogPrioritySelector is an option that specifies the syslog priority // selector. type SyslogPrioritySelector struct { - PrioritySelector func(keyvals ...interface{}) syslog.Priority + PrioritySelector func(keyvals ...interface{}) gosyslog.Priority } func (o SyslogPrioritySelector) Apply(w *syslogWriter) {