From 4c9637c8b47d21f050d163b4eae740fd4137ab50 Mon Sep 17 00:00:00 2001 From: Alex Payne Date: Fri, 12 Sep 2014 17:40:09 -0700 Subject: [PATCH 01/12] Papertrail hook, uses their UDP interface. --- hooks/papertrail/papertrail.go | 61 +++++++++++++++++++++++++++++ hooks/papertrail/papertrail_test.go | 43 ++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 hooks/papertrail/papertrail.go create mode 100644 hooks/papertrail/papertrail_test.go diff --git a/hooks/papertrail/papertrail.go b/hooks/papertrail/papertrail.go new file mode 100644 index 0000000..ca445bb --- /dev/null +++ b/hooks/papertrail/papertrail.go @@ -0,0 +1,61 @@ +package logrus_papertrail + +import ( + "fmt" + "net" + "os" + "time" + + "github.com/Sirupsen/logrus" +) + +const ( + format = "Jan 2 15:04:05" +) + +// PapertrailHook to send logs to a logging service compatible with the Papertrail API. +type PapertrailHook struct { + Host string + Port int + AppName string + UDPConn net.Conn +} + +// NewPapertrailHook creates a hook to be added to an instance of logger. +func NewPapertrailHook(host string, port int, appName string) (*PapertrailHook, error) { + conn, err := net.Dial("udp", fmt.Sprintf("%s:%d", host, port)) + return &PapertrailHook{host, port, appName, conn}, err +} + +// Fire is called when a log event is fired. +func (hook *PapertrailHook) Fire(entry *logrus.Entry) error { + defer hook.UDPConn.Close() + date := time.Now().Format(format) + + line, err := entry.String() + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err) + return err + } + + payload := fmt.Sprintf("<22> %s %s: [%s] %s", date, hook.AppName, entry.Data["level"], line) + + _, err = hook.UDPConn.Write([]byte(payload)) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to send log line to Papertrail via UDP, %v", err) + return err + } + + return nil +} + +// Levels returns the available logging levels. +func (hook *PapertrailHook) Levels() []logrus.Level { + return []logrus.Level{ + logrus.Panic, + logrus.Fatal, + logrus.Error, + logrus.Warn, + logrus.Info, + } +} diff --git a/hooks/papertrail/papertrail_test.go b/hooks/papertrail/papertrail_test.go new file mode 100644 index 0000000..a52874c --- /dev/null +++ b/hooks/papertrail/papertrail_test.go @@ -0,0 +1,43 @@ +package logrus_papertrail + +import ( + "net" + "testing" + + "github.com/Sirupsen/logrus" +) + +func TestWritingToUDP(t *testing.T) { + log := logrus.New() + port := 16661 + + addr := net.UDPAddr{ + Port: port, + IP: net.ParseIP("127.0.0.1"), + } + + c, err := net.ListenUDP("udp", &addr) + if err != nil { + t.Fatalf("ListenUDP failed: %v", err) + } + defer c.Close() + + hook, err := NewPapertrailHook("localhost", port, "test") + if err != nil { + t.Errorf("Unable to connect to local UDP server.") + } + + log.Hooks.Add(hook) + log.Info("Today was a good day.") + + var buf = make([]byte, 1500) + n, _, err := c.ReadFromUDP(buf) + + if err != nil { + t.Fatalf("Error reading data from local UDP server") + } + + if n <= 0 { + t.Errorf("Nothing written to local UDP server.") + } +} From f42d21582890f0152788f1f19853806ff7de1f55 Mon Sep 17 00:00:00 2001 From: Alex Payne Date: Fri, 12 Sep 2014 17:44:28 -0700 Subject: [PATCH 02/12] README for Papertrail hook with usage info. --- hooks/papertrail/README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 hooks/papertrail/README.md diff --git a/hooks/papertrail/README.md b/hooks/papertrail/README.md new file mode 100644 index 0000000..7ff0564 --- /dev/null +++ b/hooks/papertrail/README.md @@ -0,0 +1,22 @@ +# Papertrail Hook for Logrus + +## Usage + +You can find your Papertrail UDP port on your [Papertrail account page](https://papertrailapp.com/account/destinations). + +```go +import ( + "log/syslog" + "github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus/hooks/papertrail" +) + +func main() { + log := logrus.New() + hook, err := logrus_papertrail.NewPapertrailHook("logs.papertrailapp.com", YOUR_PAPERTRAIL_UDP_PORT, YOUR_APP_NAME) + + if err == nil { + log.Hooks.Add(hook) + } +} +``` From a93cac017b0807f9c7a5a2b899d46dc4bed0acc1 Mon Sep 17 00:00:00 2001 From: Alex Payne Date: Sat, 13 Sep 2014 11:55:08 -0700 Subject: [PATCH 03/12] Link to Papertrail hook. Format hook links consistently. --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 75a1baa..44df9ee 100644 --- a/README.md +++ b/README.md @@ -214,11 +214,14 @@ func init() { } ``` -* [`github.com/Sirupsen/logrus/hooks/airbrake`](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go). +* [`github.com/Sirupsen/logrus/hooks/airbrake`](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go) Send errors to an exception tracking service compatible with the Airbrake API. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. -* [`github.com/Sirupsen/logrus/hooks/syslog`](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go). +* [`github.com/Sirupsen/logrus/hooks/papertrail`](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go) + Send errors to the Papertrail hosted logging service via UDP. + +* [`github.com/Sirupsen/logrus/hooks/syslog`](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. @@ -298,7 +301,7 @@ The built-in logging formatters are: * `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise without colors. * *Note:* to force colored output when there is no TTY, set the `ForceColors` - field to `true`. To force no colored output even if there is a TTY set the + field to `true`. To force no colored output even if there is a TTY set the `DisableColors` field to `true` * `logrus.JSONFormatter`. Logs fields as JSON. From 0a2a97ea82b3ca56a27579a4f31f666ed638bfcf Mon Sep 17 00:00:00 2001 From: Alex Payne Date: Sat, 13 Sep 2014 11:55:21 -0700 Subject: [PATCH 04/12] Improved documentation for Papertrail hook. --- hooks/papertrail/README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/hooks/papertrail/README.md b/hooks/papertrail/README.md index 7ff0564..ae61e92 100644 --- a/hooks/papertrail/README.md +++ b/hooks/papertrail/README.md @@ -1,8 +1,14 @@ -# Papertrail Hook for Logrus +# Papertrail Hook for Logrus :walrus: + +[Papertrail](https://papertrailapp.com) provides hosted log management. Once stored in Papertrail, you can [group](http://help.papertrailapp.com/kb/how-it-works/groups/) your logs on various dimensions, [search](http://help.papertrailapp.com/kb/how-it-works/search-syntax) them, and trigger [alerts](http://help.papertrailapp.com/kb/how-it-works/alerts). + +In most deployments, you'll want to send logs to Papertrail via their [remote_syslog](http://help.papertrailapp.com/kb/configuration/configuring-centralized-logging-from-text-log-files-in-unix/) daemon, which requires no application-specific configuration. This hook is intended for relatively low-volume logging, likely in managed cloud hosting deployments where installing `remote_syslog` is not possible. ## Usage -You can find your Papertrail UDP port on your [Papertrail account page](https://papertrailapp.com/account/destinations). +You can find your Papertrail UDP port on your [Papertrail account page](https://papertrailapp.com/account/destinations). Substitute it below for `YOUR_PAPERTRAIL_UDP_PORT`. + +For `YOUR_APP_NAME`, substitute a short string that will readily identify your application or service in the logs. ```go import ( From 29c4caff543b6311ff5df0fdb9aab0424343d817 Mon Sep 17 00:00:00 2001 From: Alex Payne Date: Sat, 13 Sep 2014 13:03:15 -0700 Subject: [PATCH 05/12] Log just the message, not the stringified version of the whole log line. --- hooks/papertrail/papertrail.go | 1 + 1 file changed, 1 insertion(+) diff --git a/hooks/papertrail/papertrail.go b/hooks/papertrail/papertrail.go index ca445bb..3c12991 100644 --- a/hooks/papertrail/papertrail.go +++ b/hooks/papertrail/papertrail.go @@ -31,6 +31,7 @@ func NewPapertrailHook(host string, port int, appName string) (*PapertrailHook, func (hook *PapertrailHook) Fire(entry *logrus.Entry) error { defer hook.UDPConn.Close() date := time.Now().Format(format) + payload := fmt.Sprintf("<22> %s %s: [%s] %s", date, hook.AppName, entry.Data["level"], entry.Message) line, err := entry.String() if err != nil { From 91f92800d74be765d6933b6e313b94076e346be7 Mon Sep 17 00:00:00 2001 From: Alex Payne Date: Sat, 13 Sep 2014 13:03:40 -0700 Subject: [PATCH 06/12] Include number of bytes written as debugging output. --- hooks/papertrail/papertrail.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hooks/papertrail/papertrail.go b/hooks/papertrail/papertrail.go index 3c12991..57d07c0 100644 --- a/hooks/papertrail/papertrail.go +++ b/hooks/papertrail/papertrail.go @@ -34,6 +34,7 @@ func (hook *PapertrailHook) Fire(entry *logrus.Entry) error { payload := fmt.Sprintf("<22> %s %s: [%s] %s", date, hook.AppName, entry.Data["level"], entry.Message) line, err := entry.String() + bytesWritten, err := hook.UDPConn.Write([]byte(payload)) if err != nil { fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err) return err @@ -44,6 +45,7 @@ func (hook *PapertrailHook) Fire(entry *logrus.Entry) error { _, err = hook.UDPConn.Write([]byte(payload)) if err != nil { fmt.Fprintf(os.Stderr, "Unable to send log line to Papertrail via UDP, %v", err) + fmt.Fprintf(os.Stderr, "Unable to send log line to Papertrail via UDP. Wrote %d bytes before error: %v", bytesWritten, err) return err } From dbf7fad1dc0a62e1cba6e69bcb6437273eda1d06 Mon Sep 17 00:00:00 2001 From: Alex Payne Date: Sat, 13 Sep 2014 13:04:25 -0700 Subject: [PATCH 07/12] Don't disconnect on every Fire() --- hooks/papertrail/papertrail.go | 1 - 1 file changed, 1 deletion(-) diff --git a/hooks/papertrail/papertrail.go b/hooks/papertrail/papertrail.go index 57d07c0..add8dcd 100644 --- a/hooks/papertrail/papertrail.go +++ b/hooks/papertrail/papertrail.go @@ -29,7 +29,6 @@ func NewPapertrailHook(host string, port int, appName string) (*PapertrailHook, // Fire is called when a log event is fired. func (hook *PapertrailHook) Fire(entry *logrus.Entry) error { - defer hook.UDPConn.Close() date := time.Now().Format(format) payload := fmt.Sprintf("<22> %s %s: [%s] %s", date, hook.AppName, entry.Data["level"], entry.Message) From ced81183efb31f2453a503e5ae3195dea12d1e34 Mon Sep 17 00:00:00 2001 From: Alex Payne Date: Sat, 13 Sep 2014 13:52:24 -0700 Subject: [PATCH 08/12] Proper log levels. --- hooks/papertrail/papertrail.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hooks/papertrail/papertrail.go b/hooks/papertrail/papertrail.go index add8dcd..71c5835 100644 --- a/hooks/papertrail/papertrail.go +++ b/hooks/papertrail/papertrail.go @@ -54,10 +54,11 @@ func (hook *PapertrailHook) Fire(entry *logrus.Entry) error { // Levels returns the available logging levels. func (hook *PapertrailHook) Levels() []logrus.Level { return []logrus.Level{ - logrus.Panic, - logrus.Fatal, - logrus.Error, - logrus.Warn, - logrus.Info, + logrus.PanicLevel, + logrus.FatalLevel, + logrus.ErrorLevel, + logrus.WarnLevel, + logrus.InfoLevel, + logrus.DebugLevel, } } From e9499670227fff63a688eed6499bcca3e049ebc4 Mon Sep 17 00:00:00 2001 From: Alex Payne Date: Sat, 13 Sep 2014 13:52:51 -0700 Subject: [PATCH 09/12] Remove cruft. --- hooks/papertrail/papertrail.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/hooks/papertrail/papertrail.go b/hooks/papertrail/papertrail.go index 71c5835..48e2fea 100644 --- a/hooks/papertrail/papertrail.go +++ b/hooks/papertrail/papertrail.go @@ -32,18 +32,8 @@ func (hook *PapertrailHook) Fire(entry *logrus.Entry) error { date := time.Now().Format(format) payload := fmt.Sprintf("<22> %s %s: [%s] %s", date, hook.AppName, entry.Data["level"], entry.Message) - line, err := entry.String() bytesWritten, err := hook.UDPConn.Write([]byte(payload)) if err != nil { - fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err) - return err - } - - payload := fmt.Sprintf("<22> %s %s: [%s] %s", date, hook.AppName, entry.Data["level"], line) - - _, err = hook.UDPConn.Write([]byte(payload)) - if err != nil { - fmt.Fprintf(os.Stderr, "Unable to send log line to Papertrail via UDP, %v", err) fmt.Fprintf(os.Stderr, "Unable to send log line to Papertrail via UDP. Wrote %d bytes before error: %v", bytesWritten, err) return err } From 51770fb4eb7a407cacc5e0f04624bb45261f964b Mon Sep 17 00:00:00 2001 From: Alex Payne Date: Sat, 13 Sep 2014 13:53:20 -0700 Subject: [PATCH 10/12] Use third-party UDP testing library to tighten up test. --- hooks/papertrail/papertrail_test.go | 31 +++++++---------------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/hooks/papertrail/papertrail_test.go b/hooks/papertrail/papertrail_test.go index a52874c..96318d0 100644 --- a/hooks/papertrail/papertrail_test.go +++ b/hooks/papertrail/papertrail_test.go @@ -1,43 +1,26 @@ package logrus_papertrail import ( - "net" + "fmt" "testing" "github.com/Sirupsen/logrus" + "github.com/stvp/go-udp-testing" ) func TestWritingToUDP(t *testing.T) { - log := logrus.New() port := 16661 - - addr := net.UDPAddr{ - Port: port, - IP: net.ParseIP("127.0.0.1"), - } - - c, err := net.ListenUDP("udp", &addr) - if err != nil { - t.Fatalf("ListenUDP failed: %v", err) - } - defer c.Close() + udp.SetAddr(fmt.Sprintf(":%d", port)) hook, err := NewPapertrailHook("localhost", port, "test") if err != nil { t.Errorf("Unable to connect to local UDP server.") } + log := logrus.New() log.Hooks.Add(hook) - log.Info("Today was a good day.") - var buf = make([]byte, 1500) - n, _, err := c.ReadFromUDP(buf) - - if err != nil { - t.Fatalf("Error reading data from local UDP server") - } - - if n <= 0 { - t.Errorf("Nothing written to local UDP server.") - } + udp.ShouldReceive(t, "foo", func() { + log.Info("foo") + }) } From 29eb7b0e468d6dd346fffe832e4233c8f876a99b Mon Sep 17 00:00:00 2001 From: Alex Payne Date: Sun, 14 Sep 2014 11:50:08 -0700 Subject: [PATCH 11/12] Updated Travis CI configuration. - Move to more recent version of Go, setting aside 1.1. - Use `install` instead of `before_script` to install library dependencies, as per [documentation](http://docs.travis-ci.com/user/languages/go/). --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2efbc54..2c5ec79 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,8 @@ language: go go: - - 1.1 - 1.2 + - 1.3 - tip -before_script: +install: - go get github.com/stretchr/testify + - go get github.com/stvp/go-udp-testing From 1aa71730f7bfd27c10dc255ab6ffe2e6ae39b665 Mon Sep 17 00:00:00 2001 From: Alex Payne Date: Sun, 14 Sep 2014 11:55:49 -0700 Subject: [PATCH 12/12] Include airbrake-go dependency in Travis install step. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 2c5ec79..d5a559f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,3 +6,4 @@ go: install: - go get github.com/stretchr/testify - go get github.com/stvp/go-udp-testing + - go get github.com/tobi/airbrake-go