From 83edcb388934e80b22dd83e8799d6c3baf5eb8eb Mon Sep 17 00:00:00 2001 From: Christian Schwarz Date: Sat, 23 Sep 2017 12:58:13 +0200 Subject: [PATCH] experimental TCP hook for logrus --- cmd/config_logging.go | 36 +++++++++++++++++++++++++++++++++- cmd/logrus.go | 45 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/cmd/config_logging.go b/cmd/config_logging.go index 3157e37..cb06888 100644 --- a/cmd/config_logging.go +++ b/cmd/config_logging.go @@ -5,6 +5,7 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" "os" + "time" ) type LoggingConfig struct { @@ -12,6 +13,15 @@ type LoggingConfig struct { Level logrus.Level Format LogFormat } + TCP *TCPLoggingConfig +} + +type TCPLoggingConfig struct { + Level logrus.Level + Format LogFormat + Net string + Address string + RetryInterval time.Duration } func parseLogging(i interface{}) (c *LoggingConfig, err error) { @@ -28,6 +38,13 @@ func parseLogging(i interface{}) (c *LoggingConfig, err error) { Level string Format string } + TCP struct { + Level string + Format string + Net string + Address string + RetryInterval string `mapstructure:"retry_interval"` + } } if err = mapstructure.Decode(i, &asMap); err != nil { return nil, errors.Wrap(err, "mapstructure error") @@ -48,6 +65,20 @@ func parseLogging(i interface{}) (c *LoggingConfig, err error) { c.Stdout.Format = format } + if asMap.TCP.Address != "" { + c.TCP = &TCPLoggingConfig{} + c.TCP.Format, err = parseLogFormat(asMap.TCP.Format) + if err != nil { + return nil, errors.Wrap(err, "cannot parse log format") + } + c.TCP.Level, err = logrus.ParseLevel(asMap.TCP.Level) + if err != nil { + return nil, errors.Wrap(err, "cannot parse level") + } + c.TCP.RetryInterval, err = time.ParseDuration(asMap.TCP.RetryInterval) + c.TCP.Net, c.TCP.Address = asMap.TCP.Net, asMap.TCP.Address + } + return c, nil } @@ -96,9 +127,12 @@ func (c *LoggingConfig) MakeLogrus() (l logrus.FieldLogger) { log := logrus.New() log.Out = os.Stdout - log.Level = c.Stdout.Level + log.Level = logrus.DebugLevel // FIXTHIS IN LOGRUS log.Formatter = c.Stdout.Format.Formatter() + th := &TCPHook{Formatter: JSONFormatter{}, MinLevel: c.TCP.Level, Net: c.TCP.Net, Address: c.TCP.Address, RetryInterval: c.TCP.RetryInterval} + log.Hooks.Add(th) + return log } diff --git a/cmd/logrus.go b/cmd/logrus.go index 9010898..9519fda 100644 --- a/cmd/logrus.go +++ b/cmd/logrus.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "net" "strings" "time" ) @@ -121,6 +122,50 @@ func (f JSONFormatter) Format(e *logrus.Entry) ([]byte, error) { } +type TCPHook struct { + Formatter logrus.Formatter + MinLevel logrus.Level + Net, Address string + RetryInterval time.Duration + conn net.Conn + retry time.Time +} + +func (h *TCPHook) Levels() []logrus.Level { + for i := range logrus.AllLevels { // assume it's ordered + if logrus.AllLevels[i] == h.MinLevel { + return logrus.AllLevels[:i] + } + } + return logrus.AllLevels +} + +func (h *TCPHook) Fire(e *logrus.Entry) error { + b, err := h.Formatter.Format(e) + if err != nil { + return err + } + + if h.conn == nil { + if time.Now().Sub(h.retry) < h.RetryInterval { + return errors.New("TCP hook reconnect prohibited by retry interval") + } + h.conn, err = net.Dial(h.Net, h.Address) + if err != nil { + h.retry = time.Now() + return errors.Wrap(err, "cannot dial") + } + } + + _, err = h.conn.Write(b) + if err != nil { + h.conn.Close() + h.conn = nil + } + + return nil +} + type nopWriter int func (w nopWriter) Write(p []byte) (n int, err error) { return len(p), nil }