From b97e8d402dbc518d9750c459466b4f9d35cc8788 Mon Sep 17 00:00:00 2001 From: Peter Kieltyka Date: Tue, 23 Aug 2016 13:23:35 -0400 Subject: [PATCH 001/111] TextFormatter: brighten up the blue --- text_formatter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text_formatter.go b/text_formatter.go index cce61f2..cdd4f31 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -14,7 +14,7 @@ const ( red = 31 green = 32 yellow = 33 - blue = 34 + blue = 36 gray = 37 ) From 1726e1744a80b3d1f736a74b8b617380ade59a4c Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Sun, 5 Feb 2017 09:21:03 -0500 Subject: [PATCH 002/111] text_formatter: detect tty based on fd --- terminal_appengine.go | 2 +- terminal_notwindows.go | 14 ++++++++++---- terminal_solaris.go | 12 +++++++++--- terminal_windows.go | 14 +++++++++----- text_formatter.go | 15 ++++++++++----- 5 files changed, 39 insertions(+), 18 deletions(-) diff --git a/terminal_appengine.go b/terminal_appengine.go index 1960169..632ecbe 100644 --- a/terminal_appengine.go +++ b/terminal_appengine.go @@ -3,6 +3,6 @@ package logrus // IsTerminal returns true if stderr's file descriptor is a terminal. -func IsTerminal() bool { +func IsTerminal(f io.Writer) bool { return true } diff --git a/terminal_notwindows.go b/terminal_notwindows.go index 329038f..190297a 100644 --- a/terminal_notwindows.go +++ b/terminal_notwindows.go @@ -9,14 +9,20 @@ package logrus import ( + "io" + "os" "syscall" "unsafe" ) // IsTerminal returns true if stderr's file descriptor is a terminal. -func IsTerminal() bool { - fd := syscall.Stderr +func IsTerminal(f io.Writer) bool { var termios Termios - _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) - return err == 0 + switch v := f.(type) { + case *os.File: + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(v.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 + default: + return false + } } diff --git a/terminal_solaris.go b/terminal_solaris.go index a3c6f6e..943394c 100644 --- a/terminal_solaris.go +++ b/terminal_solaris.go @@ -9,7 +9,13 @@ import ( ) // IsTerminal returns true if the given file descriptor is a terminal. -func IsTerminal() bool { - _, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA) - return err == nil +func IsTerminal(f io.Writer) bool { + var termios Termios + switch v := f.(type) { + case *os.File: + _, err := unix.IoctlGetTermios(int(f.Fd()), unix.TCGETA) + return err == nil + default: + return false + } } diff --git a/terminal_windows.go b/terminal_windows.go index 3727e8a..67be1c0 100644 --- a/terminal_windows.go +++ b/terminal_windows.go @@ -19,9 +19,13 @@ var ( ) // IsTerminal returns true if stderr's file descriptor is a terminal. -func IsTerminal() bool { - fd := syscall.Stderr - var st uint32 - r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) - return r != 0 && e == 0 +func IsTerminal(f io.Writer) bool { + switch v := f.(type) { + case *os.File: + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 + default: + return false + } } diff --git a/text_formatter.go b/text_formatter.go index 076de5d..f75e13e 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -3,7 +3,6 @@ package logrus import ( "bytes" "fmt" - "runtime" "sort" "strings" "time" @@ -20,12 +19,10 @@ const ( var ( baseTimestamp time.Time - isTerminal bool ) func init() { baseTimestamp = time.Now() - isTerminal = IsTerminal() } type TextFormatter struct { @@ -50,6 +47,10 @@ type TextFormatter struct { // that log extremely frequently and don't use the JSON formatter this may not // be desired. DisableSorting bool + + // Whether the logger's out is to a terminal + isTerminal bool + terminalDetermined bool } func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { @@ -70,8 +71,12 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { prefixFieldClashes(entry.Data) - isColorTerminal := isTerminal && (runtime.GOOS != "windows") - isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors + if !f.terminalDetermined { + f.isTerminal = IsTerminal(entry.Logger.Out) + f.terminalDetermined = true + } + + isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors timestampFormat := f.TimestampFormat if timestampFormat == "" { From 11fbf0fa42b837cdd5b234470b53d03bad9e5041 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Sun, 5 Feb 2017 19:10:19 -0500 Subject: [PATCH 003/111] text_formatter: fix race --- examples/basic/basic.go | 1 + formatter_bench_test.go | 3 +++ text_formatter.go | 14 ++++++++------ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/examples/basic/basic.go b/examples/basic/basic.go index a1623ec..ea7e551 100644 --- a/examples/basic/basic.go +++ b/examples/basic/basic.go @@ -2,6 +2,7 @@ package main import ( "github.com/Sirupsen/logrus" + "os" ) var log = logrus.New() diff --git a/formatter_bench_test.go b/formatter_bench_test.go index c6d290c..d948158 100644 --- a/formatter_bench_test.go +++ b/formatter_bench_test.go @@ -80,11 +80,14 @@ func BenchmarkLargeJSONFormatter(b *testing.B) { } func doBenchmark(b *testing.B, formatter Formatter, fields Fields) { + logger := New() + entry := &Entry{ Time: time.Time{}, Level: InfoLevel, Message: "message", Data: fields, + Logger: logger, } var d []byte var err error diff --git a/text_formatter.go b/text_formatter.go index f75e13e..b4dffa1 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -5,6 +5,7 @@ import ( "fmt" "sort" "strings" + "sync" "time" ) @@ -49,8 +50,8 @@ type TextFormatter struct { DisableSorting bool // Whether the logger's out is to a terminal - isTerminal bool - terminalDetermined bool + isTerminal bool + terminalOnce sync.Once } func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { @@ -71,10 +72,11 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { prefixFieldClashes(entry.Data) - if !f.terminalDetermined { - f.isTerminal = IsTerminal(entry.Logger.Out) - f.terminalDetermined = true - } + f.terminalOnce.Do(func() { + if entry.Logger != nil { + f.isTerminal = IsTerminal(entry.Logger.Out) + } + }) isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors From 141e6dc6a6809c83c4fba8fecb687fd2776de5ed Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 6 Feb 2017 19:16:20 -0500 Subject: [PATCH 004/111] readme: update with example of logging to file --- README.md | 13 +++++++++++-- examples/basic/basic.go | 10 +++++++++- terminal_solaris.go | 2 +- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 206c746..3823323 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,8 @@ func init() { // Log as JSON instead of the default ASCII formatter. log.SetFormatter(&log.JSONFormatter{}) - // Output to stdout instead of the default stderr, could also be a file. + // Output to stdout instead of the default stderr + // Can be any io.Writer, see below for File example log.SetOutput(os.Stdout) // Only log the warning severity or above. @@ -138,7 +139,15 @@ var log = logrus.New() func main() { // The API for setting attributes is a little different than the package level // exported logger. See Godoc. - log.Out = os.Stderr + log.Out = os.Stdout + + // You could set this to any `io.Writer` such as a file + // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666) + // if err == nil { + // log.Out = file + // } else { + // log.Info("Failed to log to file, using default stderr") + // } log.WithFields(logrus.Fields{ "animal": "walrus", diff --git a/examples/basic/basic.go b/examples/basic/basic.go index ea7e551..ad703fc 100644 --- a/examples/basic/basic.go +++ b/examples/basic/basic.go @@ -2,7 +2,7 @@ package main import ( "github.com/Sirupsen/logrus" - "os" + // "os" ) var log = logrus.New() @@ -10,6 +10,14 @@ var log = logrus.New() func init() { log.Formatter = new(logrus.JSONFormatter) log.Formatter = new(logrus.TextFormatter) // default + + // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666) + // if err == nil { + // log.Out = file + // } else { + // log.Info("Failed to log to file, using default stderr") + // } + log.Level = logrus.DebugLevel } diff --git a/terminal_solaris.go b/terminal_solaris.go index 943394c..f3d6f96 100644 --- a/terminal_solaris.go +++ b/terminal_solaris.go @@ -13,7 +13,7 @@ func IsTerminal(f io.Writer) bool { var termios Termios switch v := f.(type) { case *os.File: - _, err := unix.IoctlGetTermios(int(f.Fd()), unix.TCGETA) + _, err := unix.IoctlGetTermios(int(v.Fd()), unix.TCGETA) return err == nil default: return false From e2fcfb2fbaf72bc70ae06375da0f4de7b049822f Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 6 Feb 2017 19:40:31 -0500 Subject: [PATCH 005/111] readme: add some entries from stale PRs --- README.md | 65 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 3823323..eaaea79 100644 --- a/README.md +++ b/README.md @@ -215,42 +215,43 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | Hook | Description | | ----- | ----------- | -| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. | | [Airbrake "legacy"](https://github.com/gemnasium/logrus-airbrake-legacy-hook) | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. | -| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. | -| [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. | -| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. | -| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. | -| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. | -| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) | -| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. | -| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` | -| [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) | -| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) | -| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem | -| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger | -| [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail | -| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar | -| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd | -| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb | -| [Influxus] (http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB] (http://influxdata.com/) | -| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb | -| [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit | -| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic | -| [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) | +| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. | | [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) | -| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka | -| [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) | +| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. | +| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic | | [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch| -| [Sumorus](https://github.com/doublefree/sumorus) | Hook for logging to [SumoLogic](https://www.sumologic.com/)| -| [Scribe](https://github.com/sagar8192/logrus-scribe-hook) | Hook for logging to [Scribe](https://github.com/facebookarchive/scribe)| -| [Logstash](https://github.com/bshuster-repo/logrus-logstash-hook) | Hook for logging to [Logstash](https://www.elastic.co/products/logstash) | -| [logz.io](https://github.com/ripcurld00d/logrus-logzio-hook) | Hook for logging to [logz.io](https://logz.io), a Log as a Service using Logstash | -| [Logmatic.io](https://github.com/logmatic/logmatic-go) | Hook for logging to [Logmatic.io](http://logmatic.io/) | -| [Pushover](https://github.com/toorop/logrus_pushover) | Send error via [Pushover](https://pushover.net) | -| [PostgreSQL](https://github.com/gemnasium/logrus-postgresql-hook) | Send logs to [PostgreSQL](http://postgresql.org) | +| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd | +| [Go-Slack](https://github.com/multiplay/go-slack) | Hook for logging to [Slack](https://slack.com) | +| [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) | +| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. | +| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger | +| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb | +| [Influxus] (http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB] (http://influxdata.com/) | +| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` | +| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka | +| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem | | [Logentrus](https://github.com/puddingfactory/logentrus) | Hook for logging to [Logentries](https://logentries.com/) | - +| [Logmatic.io](https://github.com/logmatic/logmatic-go) | Hook for logging to [Logmatic.io](http://logmatic.io/) | +| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) | +| [Logstash](https://github.com/bshuster-repo/logrus-logstash-hook) | Hook for logging to [Logstash](https://www.elastic.co/products/logstash) | +| [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail | +| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb | +| [NATS-Hook](https://github.com/rybit/nats_logrus_hook) | Hook for logging to [NATS](https://nats.io) | +| [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit | +| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. | +| [PostgreSQL](https://github.com/gemnasium/logrus-postgresql-hook) | Send logs to [PostgreSQL](http://postgresql.org) | +| [Pushover](https://github.com/toorop/logrus_pushover) | Send error via [Pushover](https://pushover.net) | +| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) | +| [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) | +| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar | +| [Scribe](https://github.com/sagar8192/logrus-scribe-hook) | Hook for logging to [Scribe](https://github.com/facebookarchive/scribe)| +| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. | +| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. | +| [Sumorus](https://github.com/doublefree/sumorus) | Hook for logging to [SumoLogic](https://www.sumologic.com/)| +| [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. | +| [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) | +| [logz.io](https://github.com/ripcurld00d/logrus-logzio-hook) | Hook for logging to [logz.io](https://logz.io), a Log as a Service using Logstash | #### Level logging From 38f1ab305781cc3dd4bf0f7d45f36d5e7db1bfc3 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 6 Feb 2017 19:40:39 -0500 Subject: [PATCH 006/111] changelog: add for 0.11.0 and 0.11.1 --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2c2bc2..47a6c4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# 0.11.1 + +* bug: fix tty detection with custom out (#471) + +# 0.11.0 + +* performance: Use bufferpool to allocate (#370) +* terminal: terminal detection for app-engine (#343) +* feature: exit handler (#375) + # 0.10.0 * feature: Add a test hook (#180) From b9def5b3c33f812a4f503538d1ee21eeb6787d6d Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 6 Feb 2017 19:44:51 -0500 Subject: [PATCH 007/111] readme: even more hooks --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index eaaea79..77941d8 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | ----- | ----------- | | [Airbrake "legacy"](https://github.com/gemnasium/logrus-airbrake-legacy-hook) | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. | | [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. | +| [Amazon Kinesis](https://github.com/evalphobia/logrus_kinesis) | Hook for logging to [Amazon Kinesis](https://aws.amazon.com/kinesis/) | | [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) | | [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. | | [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic | @@ -231,6 +232,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` | | [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka | | [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem | +| [Logentries](https://github.com/jcftang/logentriesrus) | Hook for logging to [Logentries](https://logentries.com/) | | [Logentrus](https://github.com/puddingfactory/logentrus) | Hook for logging to [Logentries](https://logentries.com/) | | [Logmatic.io](https://github.com/logmatic/logmatic-go) | Hook for logging to [Logmatic.io](http://logmatic.io/) | | [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) | @@ -248,8 +250,10 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [Scribe](https://github.com/sagar8192/logrus-scribe-hook) | Hook for logging to [Scribe](https://github.com/facebookarchive/scribe)| | [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. | | [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. | +| [Stackdriver](https://github.com/knq/sdhook) | Hook for logging to [Google Stackdriver](https://cloud.google.com/logging/) | | [Sumorus](https://github.com/doublefree/sumorus) | Hook for logging to [SumoLogic](https://www.sumologic.com/)| | [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. | +| [TraceView](https://github.com/evalphobia/logrus_appneta) | Hook for logging to [AppNeta TraceView](https://www.appneta.com/products/traceview/) | | [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) | | [logz.io](https://github.com/ripcurld00d/logrus-logzio-hook) | Hook for logging to [logz.io](https://logz.io), a Log as a Service using Logstash | From feda23452af05568da16c028ff779b66d95b80db Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 6 Feb 2017 19:51:07 -0500 Subject: [PATCH 008/111] readme: link to colorable for windows coloring --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 77941d8..a404823 100644 --- a/README.md +++ b/README.md @@ -331,8 +331,11 @@ The built-in logging formatters are: 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 - `DisableColors` field to `true` + `DisableColors` field to `true`. For Windows, see + [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable). + * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter). * `logrus.JSONFormatter`. Logs fields as JSON. + * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter). Third party logging formatters: From 080ca65fb5a85b6f2b42053b9a895d462ed0ddff Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 6 Feb 2017 19:56:22 -0500 Subject: [PATCH 009/111] readme: add section on overriding stdlib logger --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index a404823..0671464 100644 --- a/README.md +++ b/README.md @@ -384,6 +384,18 @@ srv := http.Server{ Each line written to that writer will be printed the usual way, using formatters and hooks. The level for those entries is `info`. +This means that we can override the standard library logger easily: + +``` +logger := logrus.New() +logger.Formatter = &logrus.JSONFormatter{} + +// Use logrus for standard log output +// Note that `log` here references stdlib's log +// Not logrus imported under the name `log`. +log.SetOutput(logger.Writer()) +`` + #### Rotation Log rotation is not provided with Logrus. Log rotation should be done by an From 5c8f1691bc900a172e4148d528645c7a4caf64f9 Mon Sep 17 00:00:00 2001 From: Majid Arif Siddiqui Date: Tue, 7 Feb 2017 11:28:14 +0800 Subject: [PATCH 010/111] Fixed missing imports for windows terminal --- terminal_windows.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/terminal_windows.go b/terminal_windows.go index 67be1c0..97ad4e0 100644 --- a/terminal_windows.go +++ b/terminal_windows.go @@ -8,6 +8,8 @@ package logrus import ( + "io" + "os" "syscall" "unsafe" ) From ac38cb37a804c3638225558ca89a9ee1fb0d5a5a Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 7 Feb 2017 06:40:37 -0500 Subject: [PATCH 011/111] readme: fix markdown code syntax for stdlib example --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0671464..361b333 100644 --- a/README.md +++ b/README.md @@ -386,7 +386,7 @@ and hooks. The level for those entries is `info`. This means that we can override the standard library logger easily: -``` +```go logger := logrus.New() logger.Formatter = &logrus.JSONFormatter{} @@ -394,7 +394,7 @@ logger.Formatter = &logrus.JSONFormatter{} // Note that `log` here references stdlib's log // Not logrus imported under the name `log`. log.SetOutput(logger.Writer()) -`` +``` #### Rotation From c078b1e43f58d563c74cebe63c85789e76ddb627 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 7 Feb 2017 06:49:02 -0500 Subject: [PATCH 012/111] changelog: add fixing windows terminal detection --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47a6c4b..6cee3c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.11.2 + +* bug: fix windows terminal detection (#476) + # 0.11.1 * bug: fix tty detection with custom out (#471) From 3f603f494d61c73457fb234161d8982b9f0f0b71 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 7 Feb 2017 13:34:08 -0500 Subject: [PATCH 013/111] readme: add section on default fields --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 361b333..640cf61 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,20 @@ In general, with Logrus using any of the `printf`-family functions should be seen as a hint you should add a field, however, you can still use the `printf`-family functions with Logrus. +#### Default Fields + +Often it's helpful to have fields _always_ attached to log statements in an +application or parts of one. For example, you may want to always log the +`request_id` and `user_ip` in the context of a request. Instead of writing +`log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})` on +every line, you can create a `logrus.Entry` to pass around instead: + +```go +requestLogger := log.WithFields(log.Fields{"request_id": request_id, user_ip: user_ip}) +requestLogger.Info("something happened on that request") # will log request_id and user_ip +requestLogger.Warn("something not great happened") +``` + #### Hooks You can add hooks for logging levels. For example to send errors to an exception From 169c157a103a24a5971a19366ac6293585c30804 Mon Sep 17 00:00:00 2001 From: "Aaron.L.Xu" Date: Wed, 8 Feb 2017 19:18:14 +0800 Subject: [PATCH 014/111] fix dead url for alt_exit.go --- alt_exit.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alt_exit.go b/alt_exit.go index b4c9e84..8af9063 100644 --- a/alt_exit.go +++ b/alt_exit.go @@ -1,7 +1,7 @@ package logrus // The following code was sourced and modified from the -// https://bitbucket.org/tebeka/atexit package governed by the following license: +// https://github.com/tebeka/atexit package governed by the following license: // // Copyright (c) 2012 Miki Tebeka . // From d82ae3267f00e51730a17237aa1aed5908081ec8 Mon Sep 17 00:00:00 2001 From: Darren McCleary Date: Wed, 8 Feb 2017 14:03:21 -0500 Subject: [PATCH 015/111] Fixed import --- terminal_appengine.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/terminal_appengine.go b/terminal_appengine.go index 632ecbe..e011a86 100644 --- a/terminal_appengine.go +++ b/terminal_appengine.go @@ -2,6 +2,8 @@ package logrus +import "io" + // IsTerminal returns true if stderr's file descriptor is a terminal. func IsTerminal(f io.Writer) bool { return true From 56103bcb33e17f8e6ba8c5b699e06e76678870a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=BCller?= Date: Fri, 10 Feb 2017 16:30:30 +0100 Subject: [PATCH 016/111] Fixes typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 640cf61..05915d0 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ func main() { #### Fields -Logrus encourages careful, structured logging though logging fields instead of +Logrus encourages careful, structured logging through logging fields instead of long, unparseable error messages. For example, instead of: `log.Fatalf("Failed to send event %s to topic %s with key %d")`, you should log the much more discoverable: From 6b682c59338d48511d84bf97a11e72754ae2bc6b Mon Sep 17 00:00:00 2001 From: Darren McCleary Date: Mon, 13 Feb 2017 10:56:35 -0500 Subject: [PATCH 017/111] More import fixes --- terminal_solaris.go | 1 + terminal_windows.go | 4 ++-- text_formatter_test.go | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/terminal_solaris.go b/terminal_solaris.go index f3d6f96..62096db 100644 --- a/terminal_solaris.go +++ b/terminal_solaris.go @@ -3,6 +3,7 @@ package logrus import ( + "io" "os" "golang.org/x/sys/unix" diff --git a/terminal_windows.go b/terminal_windows.go index 97ad4e0..05d2f91 100644 --- a/terminal_windows.go +++ b/terminal_windows.go @@ -8,8 +8,8 @@ package logrus import ( - "io" - "os" + "io" + "os" "syscall" "unsafe" ) diff --git a/text_formatter_test.go b/text_formatter_test.go index 107703f..879ebd2 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -3,9 +3,9 @@ package logrus import ( "bytes" "errors" + "strings" "testing" "time" - "strings" ) func TestQuoting(t *testing.T) { From cfca98e6d9c0082625feae2fe6bde8cb955c370d Mon Sep 17 00:00:00 2001 From: Ben Brooks Date: Tue, 14 Feb 2017 10:53:03 +0000 Subject: [PATCH 018/111] Add 'QuoteEmptyFields' option to TextFormatter --- text_formatter.go | 12 +++++++++--- text_formatter_test.go | 6 +++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index b4dffa1..51397cc 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -49,6 +49,9 @@ type TextFormatter struct { // be desired. DisableSorting bool + // QuoteEmptyFields will wrap empty fields in quotes if true + QuoteEmptyFields bool + // Whether the logger's out is to a terminal isTerminal bool terminalOnce sync.Once @@ -132,7 +135,10 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin } } -func needsQuoting(text string) bool { +func (f *TextFormatter) needsQuoting(text string) bool { + if f.QuoteEmptyFields && len(text) == 0 { + return true + } for _, ch := range text { if !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || @@ -155,14 +161,14 @@ func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interf func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { switch value := value.(type) { case string: - if !needsQuoting(value) { + if !f.needsQuoting(value) { b.WriteString(value) } else { fmt.Fprintf(b, "%q", value) } case error: errmsg := value.Error() - if !needsQuoting(errmsg) { + if !f.needsQuoting(errmsg) { b.WriteString(errmsg) } else { fmt.Fprintf(b, "%q", errmsg) diff --git a/text_formatter_test.go b/text_formatter_test.go index 107703f..54fc8fe 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -3,9 +3,9 @@ package logrus import ( "bytes" "errors" + "strings" "testing" "time" - "strings" ) func TestQuoting(t *testing.T) { @@ -24,6 +24,7 @@ func TestQuoting(t *testing.T) { } } + checkQuoting(false, "") checkQuoting(false, "abcd") checkQuoting(false, "v1.0") checkQuoting(false, "1234567890") @@ -32,6 +33,9 @@ func TestQuoting(t *testing.T) { checkQuoting(true, "x,y") checkQuoting(false, errors.New("invalid")) checkQuoting(true, errors.New("invalid argument")) + + tf.QuoteEmptyFields = true + checkQuoting(true, "") } func TestTimestampFormat(t *testing.T) { From b545aee819549403a5074d8ce7590c5867253409 Mon Sep 17 00:00:00 2001 From: Ben Brooks Date: Tue, 14 Feb 2017 11:51:23 +0000 Subject: [PATCH 019/111] Add TextFormatter config for custom quote runes --- text_formatter.go | 18 +++++++++++++----- text_formatter_test.go | 13 +++++++++++-- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index 51397cc..ef799f1 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -7,6 +7,7 @@ import ( "strings" "sync" "time" + "unicode/utf8" ) const ( @@ -52,9 +53,13 @@ type TextFormatter struct { // QuoteEmptyFields will wrap empty fields in quotes if true QuoteEmptyFields bool + // QuoteRune can be set to override the default quote style + QuoteRune rune + // Whether the logger's out is to a terminal - isTerminal bool - terminalOnce sync.Once + isTerminal bool + + sync.Once } func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { @@ -75,7 +80,10 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { prefixFieldClashes(entry.Data) - f.terminalOnce.Do(func() { + f.Do(func() { + if f.QuoteRune == 0 || !utf8.ValidRune(f.QuoteRune) { + f.QuoteRune = '"' + } if entry.Logger != nil { f.isTerminal = IsTerminal(entry.Logger.Out) } @@ -164,14 +172,14 @@ func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { if !f.needsQuoting(value) { b.WriteString(value) } else { - fmt.Fprintf(b, "%q", value) + fmt.Fprintf(b, "%c%v%c", f.QuoteRune, value, f.QuoteRune) } case error: errmsg := value.Error() if !f.needsQuoting(errmsg) { b.WriteString(errmsg) } else { - fmt.Fprintf(b, "%q", errmsg) + fmt.Fprintf(b, "%c%v%c", f.QuoteRune, errmsg, f.QuoteRune) } default: fmt.Fprint(b, value) diff --git a/text_formatter_test.go b/text_formatter_test.go index 54fc8fe..fce4cfe 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -14,7 +14,7 @@ func TestQuoting(t *testing.T) { checkQuoting := func(q bool, value interface{}) { b, _ := tf.Format(WithField("test", value)) idx := bytes.Index(b, ([]byte)("test=")) - cont := bytes.Contains(b[idx+5:], []byte{'"'}) + cont := bytes.ContainsRune(b[idx+5:], tf.QuoteRune) if cont != q { if q { t.Errorf("quoting expected for: %#v", value) @@ -34,6 +34,14 @@ func TestQuoting(t *testing.T) { checkQuoting(false, errors.New("invalid")) checkQuoting(true, errors.New("invalid argument")) + // Test for custom quote rune. + tf.QuoteRune = '`' + checkQuoting(false, "") + checkQuoting(false, "abcd") + checkQuoting(true, "/foobar") + checkQuoting(true, errors.New("invalid argument")) + + // Test for quoting empty fields. tf.QuoteEmptyFields = true checkQuoting(true, "") } @@ -45,7 +53,8 @@ func TestTimestampFormat(t *testing.T) { timeStart := bytes.Index(customStr, ([]byte)("time=")) timeEnd := bytes.Index(customStr, ([]byte)("level=")) timeStr := customStr[timeStart+5 : timeEnd-1] - if timeStr[0] == '"' && timeStr[len(timeStr)-1] == '"' { + if timeStr[0] == byte(customFormatter.QuoteRune) && + timeStr[len(timeStr)-1] == byte(customFormatter.QuoteRune) { timeStr = timeStr[1 : len(timeStr)-1] } if format == "" { From ca9493dc537aa9eb044a22e3eb0d151697dbe111 Mon Sep 17 00:00:00 2001 From: Mohan Raj Rajamanickam Date: Tue, 14 Feb 2017 16:37:12 -0800 Subject: [PATCH 020/111] Fix quoted key field in Readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 640cf61..5c42e3a 100644 --- a/README.md +++ b/README.md @@ -189,7 +189,7 @@ application or parts of one. For example, you may want to always log the every line, you can create a `logrus.Entry` to pass around instead: ```go -requestLogger := log.WithFields(log.Fields{"request_id": request_id, user_ip: user_ip}) +requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip}) requestLogger.Info("something happened on that request") # will log request_id and user_ip requestLogger.Warn("something not great happened") ``` From e98cd92ccfea08009a411ce5310e1797f84e3b23 Mon Sep 17 00:00:00 2001 From: Ben Brooks Date: Wed, 15 Feb 2017 13:08:26 +0000 Subject: [PATCH 021/111] Address PR comments --- text_formatter.go | 28 +++++++++++++++------------- text_formatter_test.go | 19 +++++++++++-------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index ef799f1..ba88854 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -7,7 +7,6 @@ import ( "strings" "sync" "time" - "unicode/utf8" ) const ( @@ -53,8 +52,9 @@ type TextFormatter struct { // QuoteEmptyFields will wrap empty fields in quotes if true QuoteEmptyFields bool - // QuoteRune can be set to override the default quote style - QuoteRune rune + // QuoteCharacter can be set to the override the default quoting character " + // with something else. For example: ', or `. + QuoteCharacter string // Whether the logger's out is to a terminal isTerminal bool @@ -62,6 +62,15 @@ type TextFormatter struct { sync.Once } +func (f *TextFormatter) init(entry *Entry) { + if len(f.QuoteCharacter) == 0 { + f.QuoteCharacter = "\"" + } + if entry.Logger != nil { + f.isTerminal = IsTerminal(entry.Logger.Out) + } +} + func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { var b *bytes.Buffer keys := make([]string, 0, len(entry.Data)) @@ -80,14 +89,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { prefixFieldClashes(entry.Data) - f.Do(func() { - if f.QuoteRune == 0 || !utf8.ValidRune(f.QuoteRune) { - f.QuoteRune = '"' - } - if entry.Logger != nil { - f.isTerminal = IsTerminal(entry.Logger.Out) - } - }) + f.Do(func() { f.init(entry) }) isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors @@ -172,14 +174,14 @@ func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { if !f.needsQuoting(value) { b.WriteString(value) } else { - fmt.Fprintf(b, "%c%v%c", f.QuoteRune, value, f.QuoteRune) + fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, value, f.QuoteCharacter) } case error: errmsg := value.Error() if !f.needsQuoting(errmsg) { b.WriteString(errmsg) } else { - fmt.Fprintf(b, "%c%v%c", f.QuoteRune, errmsg, f.QuoteRune) + fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, errmsg, f.QuoteCharacter) } default: fmt.Fprint(b, value) diff --git a/text_formatter_test.go b/text_formatter_test.go index fce4cfe..9793b5f 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -14,7 +14,7 @@ func TestQuoting(t *testing.T) { checkQuoting := func(q bool, value interface{}) { b, _ := tf.Format(WithField("test", value)) idx := bytes.Index(b, ([]byte)("test=")) - cont := bytes.ContainsRune(b[idx+5:], tf.QuoteRune) + cont := bytes.Contains(b[idx+5:], []byte(tf.QuoteCharacter)) if cont != q { if q { t.Errorf("quoting expected for: %#v", value) @@ -34,16 +34,23 @@ func TestQuoting(t *testing.T) { checkQuoting(false, errors.New("invalid")) checkQuoting(true, errors.New("invalid argument")) - // Test for custom quote rune. - tf.QuoteRune = '`' + // Test for custom quote character. + tf.QuoteCharacter = "`" checkQuoting(false, "") checkQuoting(false, "abcd") checkQuoting(true, "/foobar") checkQuoting(true, errors.New("invalid argument")) + // Test for multi-character quotes. + tf.QuoteCharacter = "§~±" + checkQuoting(false, "abcd") + checkQuoting(true, errors.New("invalid argument")) + // Test for quoting empty fields. tf.QuoteEmptyFields = true checkQuoting(true, "") + checkQuoting(false, "abcd") + checkQuoting(true, errors.New("invalid argument")) } func TestTimestampFormat(t *testing.T) { @@ -52,11 +59,7 @@ func TestTimestampFormat(t *testing.T) { customStr, _ := customFormatter.Format(WithField("test", "test")) timeStart := bytes.Index(customStr, ([]byte)("time=")) timeEnd := bytes.Index(customStr, ([]byte)("level=")) - timeStr := customStr[timeStart+5 : timeEnd-1] - if timeStr[0] == byte(customFormatter.QuoteRune) && - timeStr[len(timeStr)-1] == byte(customFormatter.QuoteRune) { - timeStr = timeStr[1 : len(timeStr)-1] - } + timeStr := customStr[timeStart+5+len(customFormatter.QuoteCharacter) : timeEnd-1-len(customFormatter.QuoteCharacter)] if format == "" { format = time.RFC3339 } From 7f4b1adc791766938c29457bed0703fb9134421a Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Wed, 15 Feb 2017 11:43:24 -0500 Subject: [PATCH 022/111] changelog: add entries for formatter --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cee3c9..a6c83d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# Unreleased + +* formatter: configure quoting of empty values (#484) +* formatter: configure quoting character (default is `"`) (#484) + # 0.11.2 * bug: fix windows terminal detection (#476) From 9fd28e6ccafc44cb1d38cf3059d0c79d3f33cb66 Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Fri, 17 Feb 2017 14:09:07 +0000 Subject: [PATCH 023/111] Add Discordrus, a hook for Discord, to README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 640cf61..1641d4e 100644 --- a/README.md +++ b/README.md @@ -235,6 +235,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) | | [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. | | [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic | +| [Discordrus](https://github.com/kz/discordrus) | Hook for logging to [Discord](https://discordapp.com/) | | [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch| | [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd | | [Go-Slack](https://github.com/multiplay/go-slack) | Hook for logging to [Slack](https://slack.com) | From f9d4a063d13843279f4c2306fdd7442d5bf22e82 Mon Sep 17 00:00:00 2001 From: Denis Barakhtanov Date: Fri, 24 Feb 2017 10:16:21 +1100 Subject: [PATCH 024/111] Fixed compilation for Solaris There are missing "io" import and unused variable Releated issues #471 #289 --- terminal_solaris.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terminal_solaris.go b/terminal_solaris.go index f3d6f96..3c86b1a 100644 --- a/terminal_solaris.go +++ b/terminal_solaris.go @@ -3,6 +3,7 @@ package logrus import ( + "io" "os" "golang.org/x/sys/unix" @@ -10,7 +11,6 @@ import ( // IsTerminal returns true if the given file descriptor is a terminal. func IsTerminal(f io.Writer) bool { - var termios Termios switch v := f.(type) { case *os.File: _, err := unix.IoctlGetTermios(int(v.Fd()), unix.TCGETA) From 96acd6ab0441b5950c93da1841bc255115ccb3f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=80=E9=9B=B6=E6=9D=91=E9=95=BF?= Date: Fri, 24 Feb 2017 11:58:06 +0800 Subject: [PATCH 025/111] add import for example --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 640cf61..6531713 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ application, you can also create an instance of the `logrus` Logger: package main import ( + "os" "github.com/Sirupsen/logrus" ) From 4058491e25742dd6056d5b055a0e9c6f74356c58 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 27 Feb 2017 07:07:31 -0500 Subject: [PATCH 026/111] changelog: add 481 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6c83d7..e22b05b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * formatter: configure quoting of empty values (#484) * formatter: configure quoting character (default is `"`) (#484) +* bug: fix not importing io correctly in non-linux environments (#481) # 0.11.2 From dc71819687aaddfe31d7a2044de8d61ca7812da1 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 27 Feb 2017 07:07:50 -0500 Subject: [PATCH 027/111] release 0.11.3 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e22b05b..7b85e69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# Unreleased +# 0.11.3 * formatter: configure quoting of empty values (#484) * formatter: configure quoting character (default is `"`) (#484) From 0208149b40d863d2c1a2f8fe5753096a9cf2cc8b Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 27 Feb 2017 07:44:09 -0500 Subject: [PATCH 028/111] changelog: 0.11.4 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b85e69..4c9418f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.11.4 + +* bug: fix undefined variable on solaris (#493) + # 0.11.3 * formatter: configure quoting of empty values (#484) From add54587ab0136faecacc0f8925f7187fe16e82f Mon Sep 17 00:00:00 2001 From: "Beau N. Brewer" Date: Wed, 1 Mar 2017 12:26:05 -0700 Subject: [PATCH 029/111] Update README.md Added AWS Firehose hook. This provides us with a nice log steam to S3 workflow with very little effort. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 640cf61..34df955 100644 --- a/README.md +++ b/README.md @@ -236,6 +236,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. | | [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic | | [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch| +| [Firehose](https://github.com/beaubrewer/firehose) | Hook for logging to [Amazon Firehose](https://aws.amazon.com/kinesis/firehose/) | [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd | | [Go-Slack](https://github.com/multiplay/go-slack) | Hook for logging to [Slack](https://slack.com) | | [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) | From 1fccefa2f44159e91a6d0094b24f90a82a890ee7 Mon Sep 17 00:00:00 2001 From: David Glasser Date: Mon, 20 Jun 2016 15:55:49 -0700 Subject: [PATCH 030/111] Add Writer and WriterLevel to Entry This lets you do things like: cmd := exec.Command("command") stdout := logrus.WithField("fd", "stdout").Writer() defer stdout.Close() stderr := logrus.WithField("fd", "stderr").Writer() defer stderr.Close() cmd.Stdout = stdout cmd.Stderr = stderr --- logrus_test.go | 25 +++++++++++++++++++++++++ writer.go | 29 +++++++++++++++++++---------- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/logrus_test.go b/logrus_test.go index bfc4780..78cbc28 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -359,3 +359,28 @@ func TestLogrusInterface(t *testing.T) { e := logger.WithField("another", "value") fn(e) } + +// Implements io.Writer using channels for synchronization, so we can wait on +// the Entry.Writer goroutine to write in a non-racey way. This does assume that +// there is a single call to Logger.Out for each message. +type channelWriter chan []byte + +func (cw channelWriter) Write(p []byte) (int, error) { + cw <- p + return len(p), nil +} + +func TestEntryWriter(t *testing.T) { + cw := channelWriter(make(chan []byte, 1)) + log := New() + log.Out = cw + log.Formatter = new(JSONFormatter) + log.WithField("foo", "bar").WriterLevel(WarnLevel).Write([]byte("hello\n")) + + bs := <-cw + var fields Fields + err := json.Unmarshal(bs, &fields) + assert.Nil(t, err) + assert.Equal(t, fields["foo"], "bar") + assert.Equal(t, fields["level"], "warning") +} diff --git a/writer.go b/writer.go index f74d2aa..7bdebed 100644 --- a/writer.go +++ b/writer.go @@ -11,39 +11,48 @@ func (logger *Logger) Writer() *io.PipeWriter { } func (logger *Logger) WriterLevel(level Level) *io.PipeWriter { + return NewEntry(logger).WriterLevel(level) +} + +func (entry *Entry) Writer() *io.PipeWriter { + return entry.WriterLevel(InfoLevel) +} + +func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { reader, writer := io.Pipe() var printFunc func(args ...interface{}) + switch level { case DebugLevel: - printFunc = logger.Debug + printFunc = entry.Debug case InfoLevel: - printFunc = logger.Info + printFunc = entry.Info case WarnLevel: - printFunc = logger.Warn + printFunc = entry.Warn case ErrorLevel: - printFunc = logger.Error + printFunc = entry.Error case FatalLevel: - printFunc = logger.Fatal + printFunc = entry.Fatal case PanicLevel: - printFunc = logger.Panic + printFunc = entry.Panic default: - printFunc = logger.Print + printFunc = entry.Print } - go logger.writerScanner(reader, printFunc) + go entry.writerScanner(reader, printFunc) runtime.SetFinalizer(writer, writerFinalizer) return writer } -func (logger *Logger) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) { +func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) { scanner := bufio.NewScanner(reader) for scanner.Scan() { printFunc(scanner.Text()) } if err := scanner.Err(); err != nil { - logger.Errorf("Error while reading from Writer: %s", err) + entry.Errorf("Error while reading from Writer: %s", err) } reader.Close() } From f0542780a2209e75115bc66df91e28c6e7ea34eb Mon Sep 17 00:00:00 2001 From: Ivan Borshukov Date: Tue, 7 Mar 2017 18:02:20 +0200 Subject: [PATCH 031/111] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 640cf61..93ed040 100644 --- a/README.md +++ b/README.md @@ -421,7 +421,7 @@ entries. It should not be a feature of the application-level logger. | Tool | Description | | ---- | ----------- | |[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.| -|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper arround Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) | +|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) | #### Testing From 924f89f0e335c376a43251e732faea01bed6390a Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Wed, 8 Mar 2017 15:37:04 -0500 Subject: [PATCH 032/111] changelog for #372 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c9418f..cfec21d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# Unreleased + +* feature: add writer and writerlevel to entry (#372) + # 0.11.4 * bug: fix undefined variable on solaris (#493) From ba1b36c82c5e05c4f912a88eab0dcd91a171688f Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 14 Mar 2017 15:23:53 -0400 Subject: [PATCH 033/111] release 0.11.5 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfec21d..747e4d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# Unreleased +# 0.11.5 * feature: add writer and writerlevel to entry (#372) From 6054749f370e8e7ca0e6c73762d4cefb45cbc3f1 Mon Sep 17 00:00:00 2001 From: Alexey Remizov Date: Wed, 22 Mar 2017 17:09:05 +0300 Subject: [PATCH 034/111] Fixed typo in the comment --- json_formatter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json_formatter.go b/json_formatter.go index 266554e..753d6a2 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -35,7 +35,7 @@ type JSONFormatter struct { // FieldMap: FieldMap{ // FieldKeyTime: "@timestamp", // FieldKeyLevel: "@level", - // FieldKeyLevel: "@message", + // FieldKeyMsg: "@message", // }, // } FieldMap FieldMap From fec838f389854910bd1e9a7ea77ddf189f11fc2f Mon Sep 17 00:00:00 2001 From: Paul Litvak Date: Wed, 22 Mar 2017 22:32:29 +0200 Subject: [PATCH 035/111] Added SQS Hook --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c322876..0a545f0 100644 --- a/README.md +++ b/README.md @@ -273,6 +273,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [TraceView](https://github.com/evalphobia/logrus_appneta) | Hook for logging to [AppNeta TraceView](https://www.appneta.com/products/traceview/) | | [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) | | [logz.io](https://github.com/ripcurld00d/logrus-logzio-hook) | Hook for logging to [logz.io](https://logz.io), a Log as a Service using Logstash | +| [SQS-Hook](https://github.com/tsarpaul/logrus_sqs) | Hook for logging to [Amazon Simple Queue Service (SQS)](https://aws.amazon.com/sqs/) | #### Level logging From 3bcb09397d6d88e7676a9bc8433ca11ba5304837 Mon Sep 17 00:00:00 2001 From: DmitriyMV Date: Thu, 23 Mar 2017 19:13:19 +0300 Subject: [PATCH 036/111] This commit fixes data race using atomics. We switch type of level from uint8 to uint32 but due memory alignment on most platforms it will not result in any additional memory. --- entry.go | 36 ++++++++++++++++++------------------ exported.go | 4 ++-- logger.go | 51 ++++++++++++++++++++++++++++++--------------------- logrus.go | 2 +- 4 files changed, 51 insertions(+), 42 deletions(-) diff --git a/entry.go b/entry.go index 4edbe7a..320e5d5 100644 --- a/entry.go +++ b/entry.go @@ -126,7 +126,7 @@ func (entry Entry) log(level Level, msg string) { } func (entry *Entry) Debug(args ...interface{}) { - if entry.Logger.Level >= DebugLevel { + if entry.Logger.level() >= DebugLevel { entry.log(DebugLevel, fmt.Sprint(args...)) } } @@ -136,13 +136,13 @@ func (entry *Entry) Print(args ...interface{}) { } func (entry *Entry) Info(args ...interface{}) { - if entry.Logger.Level >= InfoLevel { + if entry.Logger.level() >= InfoLevel { entry.log(InfoLevel, fmt.Sprint(args...)) } } func (entry *Entry) Warn(args ...interface{}) { - if entry.Logger.Level >= WarnLevel { + if entry.Logger.level() >= WarnLevel { entry.log(WarnLevel, fmt.Sprint(args...)) } } @@ -152,20 +152,20 @@ func (entry *Entry) Warning(args ...interface{}) { } func (entry *Entry) Error(args ...interface{}) { - if entry.Logger.Level >= ErrorLevel { + if entry.Logger.level() >= ErrorLevel { entry.log(ErrorLevel, fmt.Sprint(args...)) } } func (entry *Entry) Fatal(args ...interface{}) { - if entry.Logger.Level >= FatalLevel { + if entry.Logger.level() >= FatalLevel { entry.log(FatalLevel, fmt.Sprint(args...)) } Exit(1) } func (entry *Entry) Panic(args ...interface{}) { - if entry.Logger.Level >= PanicLevel { + if entry.Logger.level() >= PanicLevel { entry.log(PanicLevel, fmt.Sprint(args...)) } panic(fmt.Sprint(args...)) @@ -174,13 +174,13 @@ func (entry *Entry) Panic(args ...interface{}) { // Entry Printf family functions func (entry *Entry) Debugf(format string, args ...interface{}) { - if entry.Logger.Level >= DebugLevel { + if entry.Logger.level() >= DebugLevel { entry.Debug(fmt.Sprintf(format, args...)) } } func (entry *Entry) Infof(format string, args ...interface{}) { - if entry.Logger.Level >= InfoLevel { + if entry.Logger.level() >= InfoLevel { entry.Info(fmt.Sprintf(format, args...)) } } @@ -190,7 +190,7 @@ func (entry *Entry) Printf(format string, args ...interface{}) { } func (entry *Entry) Warnf(format string, args ...interface{}) { - if entry.Logger.Level >= WarnLevel { + if entry.Logger.level() >= WarnLevel { entry.Warn(fmt.Sprintf(format, args...)) } } @@ -200,20 +200,20 @@ func (entry *Entry) Warningf(format string, args ...interface{}) { } func (entry *Entry) Errorf(format string, args ...interface{}) { - if entry.Logger.Level >= ErrorLevel { + if entry.Logger.level() >= ErrorLevel { entry.Error(fmt.Sprintf(format, args...)) } } func (entry *Entry) Fatalf(format string, args ...interface{}) { - if entry.Logger.Level >= FatalLevel { + if entry.Logger.level() >= FatalLevel { entry.Fatal(fmt.Sprintf(format, args...)) } Exit(1) } func (entry *Entry) Panicf(format string, args ...interface{}) { - if entry.Logger.Level >= PanicLevel { + if entry.Logger.level() >= PanicLevel { entry.Panic(fmt.Sprintf(format, args...)) } } @@ -221,13 +221,13 @@ func (entry *Entry) Panicf(format string, args ...interface{}) { // Entry Println family functions func (entry *Entry) Debugln(args ...interface{}) { - if entry.Logger.Level >= DebugLevel { + if entry.Logger.level() >= DebugLevel { entry.Debug(entry.sprintlnn(args...)) } } func (entry *Entry) Infoln(args ...interface{}) { - if entry.Logger.Level >= InfoLevel { + if entry.Logger.level() >= InfoLevel { entry.Info(entry.sprintlnn(args...)) } } @@ -237,7 +237,7 @@ func (entry *Entry) Println(args ...interface{}) { } func (entry *Entry) Warnln(args ...interface{}) { - if entry.Logger.Level >= WarnLevel { + if entry.Logger.level() >= WarnLevel { entry.Warn(entry.sprintlnn(args...)) } } @@ -247,20 +247,20 @@ func (entry *Entry) Warningln(args ...interface{}) { } func (entry *Entry) Errorln(args ...interface{}) { - if entry.Logger.Level >= ErrorLevel { + if entry.Logger.level() >= ErrorLevel { entry.Error(entry.sprintlnn(args...)) } } func (entry *Entry) Fatalln(args ...interface{}) { - if entry.Logger.Level >= FatalLevel { + if entry.Logger.level() >= FatalLevel { entry.Fatal(entry.sprintlnn(args...)) } Exit(1) } func (entry *Entry) Panicln(args ...interface{}) { - if entry.Logger.Level >= PanicLevel { + if entry.Logger.level() >= PanicLevel { entry.Panic(entry.sprintlnn(args...)) } } diff --git a/exported.go b/exported.go index 9a0120a..1aeaa90 100644 --- a/exported.go +++ b/exported.go @@ -31,14 +31,14 @@ func SetFormatter(formatter Formatter) { func SetLevel(level Level) { std.mu.Lock() defer std.mu.Unlock() - std.Level = level + std.setLevel(level) } // GetLevel returns the standard logger level. func GetLevel() Level { std.mu.Lock() defer std.mu.Unlock() - return std.Level + return std.level() } // AddHook adds a hook to the standard logger hooks. diff --git a/logger.go b/logger.go index b769f3d..370fff5 100644 --- a/logger.go +++ b/logger.go @@ -4,6 +4,7 @@ import ( "io" "os" "sync" + "sync/atomic" ) type Logger struct { @@ -112,7 +113,7 @@ func (logger *Logger) WithError(err error) *Entry { } func (logger *Logger) Debugf(format string, args ...interface{}) { - if logger.Level >= DebugLevel { + if logger.level() >= DebugLevel { entry := logger.newEntry() entry.Debugf(format, args...) logger.releaseEntry(entry) @@ -120,7 +121,7 @@ func (logger *Logger) Debugf(format string, args ...interface{}) { } func (logger *Logger) Infof(format string, args ...interface{}) { - if logger.Level >= InfoLevel { + if logger.level() >= InfoLevel { entry := logger.newEntry() entry.Infof(format, args...) logger.releaseEntry(entry) @@ -134,7 +135,7 @@ func (logger *Logger) Printf(format string, args ...interface{}) { } func (logger *Logger) Warnf(format string, args ...interface{}) { - if logger.Level >= WarnLevel { + if logger.level() >= WarnLevel { entry := logger.newEntry() entry.Warnf(format, args...) logger.releaseEntry(entry) @@ -142,7 +143,7 @@ func (logger *Logger) Warnf(format string, args ...interface{}) { } func (logger *Logger) Warningf(format string, args ...interface{}) { - if logger.Level >= WarnLevel { + if logger.level() >= WarnLevel { entry := logger.newEntry() entry.Warnf(format, args...) logger.releaseEntry(entry) @@ -150,7 +151,7 @@ func (logger *Logger) Warningf(format string, args ...interface{}) { } func (logger *Logger) Errorf(format string, args ...interface{}) { - if logger.Level >= ErrorLevel { + if logger.level() >= ErrorLevel { entry := logger.newEntry() entry.Errorf(format, args...) logger.releaseEntry(entry) @@ -158,7 +159,7 @@ func (logger *Logger) Errorf(format string, args ...interface{}) { } func (logger *Logger) Fatalf(format string, args ...interface{}) { - if logger.Level >= FatalLevel { + if logger.level() >= FatalLevel { entry := logger.newEntry() entry.Fatalf(format, args...) logger.releaseEntry(entry) @@ -167,7 +168,7 @@ func (logger *Logger) Fatalf(format string, args ...interface{}) { } func (logger *Logger) Panicf(format string, args ...interface{}) { - if logger.Level >= PanicLevel { + if logger.level() >= PanicLevel { entry := logger.newEntry() entry.Panicf(format, args...) logger.releaseEntry(entry) @@ -175,7 +176,7 @@ func (logger *Logger) Panicf(format string, args ...interface{}) { } func (logger *Logger) Debug(args ...interface{}) { - if logger.Level >= DebugLevel { + if logger.level() >= DebugLevel { entry := logger.newEntry() entry.Debug(args...) logger.releaseEntry(entry) @@ -183,7 +184,7 @@ func (logger *Logger) Debug(args ...interface{}) { } func (logger *Logger) Info(args ...interface{}) { - if logger.Level >= InfoLevel { + if logger.level() >= InfoLevel { entry := logger.newEntry() entry.Info(args...) logger.releaseEntry(entry) @@ -197,7 +198,7 @@ func (logger *Logger) Print(args ...interface{}) { } func (logger *Logger) Warn(args ...interface{}) { - if logger.Level >= WarnLevel { + if logger.level() >= WarnLevel { entry := logger.newEntry() entry.Warn(args...) logger.releaseEntry(entry) @@ -205,7 +206,7 @@ func (logger *Logger) Warn(args ...interface{}) { } func (logger *Logger) Warning(args ...interface{}) { - if logger.Level >= WarnLevel { + if logger.level() >= WarnLevel { entry := logger.newEntry() entry.Warn(args...) logger.releaseEntry(entry) @@ -213,7 +214,7 @@ func (logger *Logger) Warning(args ...interface{}) { } func (logger *Logger) Error(args ...interface{}) { - if logger.Level >= ErrorLevel { + if logger.level() >= ErrorLevel { entry := logger.newEntry() entry.Error(args...) logger.releaseEntry(entry) @@ -221,7 +222,7 @@ func (logger *Logger) Error(args ...interface{}) { } func (logger *Logger) Fatal(args ...interface{}) { - if logger.Level >= FatalLevel { + if logger.level() >= FatalLevel { entry := logger.newEntry() entry.Fatal(args...) logger.releaseEntry(entry) @@ -230,7 +231,7 @@ func (logger *Logger) Fatal(args ...interface{}) { } func (logger *Logger) Panic(args ...interface{}) { - if logger.Level >= PanicLevel { + if logger.level() >= PanicLevel { entry := logger.newEntry() entry.Panic(args...) logger.releaseEntry(entry) @@ -238,7 +239,7 @@ func (logger *Logger) Panic(args ...interface{}) { } func (logger *Logger) Debugln(args ...interface{}) { - if logger.Level >= DebugLevel { + if logger.level() >= DebugLevel { entry := logger.newEntry() entry.Debugln(args...) logger.releaseEntry(entry) @@ -246,7 +247,7 @@ func (logger *Logger) Debugln(args ...interface{}) { } func (logger *Logger) Infoln(args ...interface{}) { - if logger.Level >= InfoLevel { + if logger.level() >= InfoLevel { entry := logger.newEntry() entry.Infoln(args...) logger.releaseEntry(entry) @@ -260,7 +261,7 @@ func (logger *Logger) Println(args ...interface{}) { } func (logger *Logger) Warnln(args ...interface{}) { - if logger.Level >= WarnLevel { + if logger.level() >= WarnLevel { entry := logger.newEntry() entry.Warnln(args...) logger.releaseEntry(entry) @@ -268,7 +269,7 @@ func (logger *Logger) Warnln(args ...interface{}) { } func (logger *Logger) Warningln(args ...interface{}) { - if logger.Level >= WarnLevel { + if logger.level() >= WarnLevel { entry := logger.newEntry() entry.Warnln(args...) logger.releaseEntry(entry) @@ -276,7 +277,7 @@ func (logger *Logger) Warningln(args ...interface{}) { } func (logger *Logger) Errorln(args ...interface{}) { - if logger.Level >= ErrorLevel { + if logger.level() >= ErrorLevel { entry := logger.newEntry() entry.Errorln(args...) logger.releaseEntry(entry) @@ -284,7 +285,7 @@ func (logger *Logger) Errorln(args ...interface{}) { } func (logger *Logger) Fatalln(args ...interface{}) { - if logger.Level >= FatalLevel { + if logger.level() >= FatalLevel { entry := logger.newEntry() entry.Fatalln(args...) logger.releaseEntry(entry) @@ -293,7 +294,7 @@ func (logger *Logger) Fatalln(args ...interface{}) { } func (logger *Logger) Panicln(args ...interface{}) { - if logger.Level >= PanicLevel { + if logger.level() >= PanicLevel { entry := logger.newEntry() entry.Panicln(args...) logger.releaseEntry(entry) @@ -306,3 +307,11 @@ func (logger *Logger) Panicln(args ...interface{}) { func (logger *Logger) SetNoLock() { logger.mu.Disable() } + +func (logger *Logger) level() Level { + return Level(atomic.LoadUint32((*uint32)(&logger.Level))) +} + +func (logger *Logger) setLevel(level Level) { + atomic.StoreUint32((*uint32)(&logger.Level), uint32(level)) +} diff --git a/logrus.go b/logrus.go index e596691..dd38999 100644 --- a/logrus.go +++ b/logrus.go @@ -10,7 +10,7 @@ import ( type Fields map[string]interface{} // Level type -type Level uint8 +type Level uint32 // Convert the Level to a string. E.g. PanicLevel becomes "panic". func (level Level) String() string { From 6223de399c95ccd2e0eec42c47f2f2f6097dcaaf Mon Sep 17 00:00:00 2001 From: Jay Qin Date: Thu, 23 Mar 2017 09:50:46 -0700 Subject: [PATCH 037/111] Fix the JSONFormatter example comment --- json_formatter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json_formatter.go b/json_formatter.go index 266554e..753d6a2 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -35,7 +35,7 @@ type JSONFormatter struct { // FieldMap: FieldMap{ // FieldKeyTime: "@timestamp", // FieldKeyLevel: "@level", - // FieldKeyLevel: "@message", + // FieldKeyMsg: "@message", // }, // } FieldMap FieldMap From ab2de9ffb1df145ad7ab5326354909854534fa1d Mon Sep 17 00:00:00 2001 From: Jonathan Hall Date: Thu, 20 Apr 2017 09:29:15 +0200 Subject: [PATCH 038/111] Fix a few comments to conform to Go standards --- hooks/test/test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hooks/test/test.go b/hooks/test/test.go index 0688125..0190ba0 100644 --- a/hooks/test/test.go +++ b/hooks/test/test.go @@ -6,12 +6,12 @@ import ( "github.com/Sirupsen/logrus" ) -// test.Hook is a hook designed for dealing with logs in test scenarios. +// Hook is a hook designed for dealing with logs in test scenarios. type Hook struct { Entries []*logrus.Entry } -// Installs a test hook for the global logger. +// NewGlobal installs a test hook for the global logger. func NewGlobal() *Hook { hook := new(Hook) @@ -21,7 +21,7 @@ func NewGlobal() *Hook { } -// Installs a test hook for a given local logger. +// NewLocal installs a test hook for a given local logger. func NewLocal(logger *logrus.Logger) *Hook { hook := new(Hook) @@ -31,7 +31,7 @@ func NewLocal(logger *logrus.Logger) *Hook { } -// Creates a discarding logger and installs the test hook. +// NewNullLogger creates a discarding logger and installs the test hook. func NewNullLogger() (*logrus.Logger, *Hook) { logger := logrus.New() From cc6ca6f305195f1bc8bef5ae2f82cdc8b08e5265 Mon Sep 17 00:00:00 2001 From: Jonathan Hall Date: Thu, 20 Apr 2017 09:33:10 +0200 Subject: [PATCH 039/111] Protect test entries with a mutex --- hooks/test/test.go | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/hooks/test/test.go b/hooks/test/test.go index 0190ba0..78d75ff 100644 --- a/hooks/test/test.go +++ b/hooks/test/test.go @@ -2,6 +2,7 @@ package test import ( "io/ioutil" + "sync" "github.com/Sirupsen/logrus" ) @@ -9,6 +10,7 @@ import ( // Hook is a hook designed for dealing with logs in test scenarios. type Hook struct { Entries []*logrus.Entry + mu sync.RWMutex } // NewGlobal installs a test hook for the global logger. @@ -42,6 +44,8 @@ func NewNullLogger() (*logrus.Logger, *Hook) { } func (t *Hook) Fire(e *logrus.Entry) error { + t.mu.Lock() + defer t.mu.Unlock() t.Entries = append(t.Entries, e) return nil } @@ -51,17 +55,35 @@ func (t *Hook) Levels() []logrus.Level { } // LastEntry returns the last entry that was logged or nil. -func (t *Hook) LastEntry() (l *logrus.Entry) { - - if i := len(t.Entries) - 1; i < 0 { +func (t *Hook) LastEntry() *logrus.Entry { + t.mu.RLock() + defer t.mu.RUnlock() + i := len(t.Entries) - 1 + if i < 0 { return nil - } else { - return t.Entries[i] } + // Make a copy, for safety + e := *t.Entries[i] + return &e +} +// AllEntries returns all entries that were logged. +func (t *Hook) AllEntries() []*logrus.Entry { + t.mu.RLock() + defer t.mu.RUnlock() + // Make a copy so the returned value won't race with future log requests + entries := make([]*logrus.Entry, len(t.Entries)) + for i, entry := range t.Entries { + // Make a copy, for safety + e := *entry + entries[i] = &e + } + return entries } // Reset removes all Entries from this test hook. func (t *Hook) Reset() { + t.mu.Lock() + defer t.mu.Unlock() t.Entries = make([]*logrus.Entry, 0) } From 7e4197a54fa63f4dcc875450bfa4e8609f3bfecc Mon Sep 17 00:00:00 2001 From: Jonathan Hall Date: Thu, 20 Apr 2017 09:37:32 +0200 Subject: [PATCH 040/111] Add a small warning comment --- hooks/test/test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hooks/test/test.go b/hooks/test/test.go index 78d75ff..a94a6f9 100644 --- a/hooks/test/test.go +++ b/hooks/test/test.go @@ -9,6 +9,9 @@ import ( // Hook is a hook designed for dealing with logs in test scenarios. type Hook struct { + // Entries is an array of all entries that have been received by this hook. + // For safe access, use the AllEntries() method, rather than reading this + // value directly. Entries []*logrus.Entry mu sync.RWMutex } From dba7a9fd255a2f3c79b3c8d85c17d73a799e846d Mon Sep 17 00:00:00 2001 From: Jonathan Hall Date: Thu, 20 Apr 2017 09:50:47 +0200 Subject: [PATCH 041/111] Add Go 1.8 to the testing matrix And use the .x versions, so the latest versions of each Go release is used. --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 804c569..ef00c00 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,8 @@ language: go go: - - 1.6 - - 1.7 + - 1.6.x + - 1.7.x + - 1.8.x - tip install: - go get -t ./... From 012baad06cfa72c112c62c99cc4e7ed9e3ee8037 Mon Sep 17 00:00:00 2001 From: Urjit Singh Bhatia Date: Tue, 2 May 2017 10:47:34 -0700 Subject: [PATCH 042/111] Fix firehose hook url Firehose hook url was broken. Correct url is: https://github.com/beaubrewer/logrus_firehose --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c322876..0572d7a 100644 --- a/README.md +++ b/README.md @@ -238,7 +238,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic | | [Discordrus](https://github.com/kz/discordrus) | Hook for logging to [Discord](https://discordapp.com/) | | [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch| -| [Firehose](https://github.com/beaubrewer/firehose) | Hook for logging to [Amazon Firehose](https://aws.amazon.com/kinesis/firehose/) +| [Firehose](https://github.com/beaubrewer/logrus_firehose) | Hook for logging to [Amazon Firehose](https://aws.amazon.com/kinesis/firehose/) | [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd | | [Go-Slack](https://github.com/multiplay/go-slack) | Hook for logging to [Slack](https://slack.com) | | [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) | From 8aa045e2957068e39eac9f342fc48459d2c389c5 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Wed, 3 May 2017 17:23:24 +0100 Subject: [PATCH 043/111] readme: Fix md links There were a couple of spaces between ']' and '(' causing the links to be misrendered. Signed-off-by: Damien Lespiau --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c322876..2d7b14d 100644 --- a/README.md +++ b/README.md @@ -245,7 +245,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. | | [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger | | [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb | -| [Influxus] (http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB] (http://influxdata.com/) | +| [Influxus](http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB](http://influxdata.com/) | | [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` | | [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka | | [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem | From e3715134c9aa63f8ee511fbf1a52f06d0e053c87 Mon Sep 17 00:00:00 2001 From: Mattias Lundell Date: Fri, 5 May 2017 14:18:10 +0200 Subject: [PATCH 044/111] Fixed small typo in example. --- json_formatter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json_formatter.go b/json_formatter.go index 266554e..753d6a2 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -35,7 +35,7 @@ type JSONFormatter struct { // FieldMap: FieldMap{ // FieldKeyTime: "@timestamp", // FieldKeyLevel: "@level", - // FieldKeyLevel: "@message", + // FieldKeyMsg: "@message", // }, // } FieldMap FieldMap From 8df512bed53e580bf89df9683a2da0bd08d2f962 Mon Sep 17 00:00:00 2001 From: JodeZer Date: Thu, 11 May 2017 18:19:16 +0800 Subject: [PATCH 045/111] fix readme example go import path --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b6195df..65eec9c 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ The simplest way to use Logrus is simply the package-level exported logger: package main import ( - log "github.com/Sirupsen/logrus" + log "github.com/sirupsen/logrus" ) func main() { @@ -71,7 +71,7 @@ func main() { ``` Note that it's completely api-compatible with the stdlib logger, so you can -replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"` +replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"` and you'll now have the flexibility of Logrus. You can customize it all you want: @@ -80,7 +80,7 @@ package main import ( "os" - log "github.com/Sirupsen/logrus" + log "github.com/sirupsen/logrus" ) func init() { @@ -131,7 +131,7 @@ package main import ( "os" - "github.com/Sirupsen/logrus" + "github.com/sirupsen/logrus" ) // Create a new instance of the logger. You can have any number of instances. @@ -206,9 +206,9 @@ Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in ```go import ( - log "github.com/Sirupsen/logrus" + log "github.com/sirupsen/logrus" "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake" - logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" + logrus_syslog "github.com/sirupsen/logrus/hooks/syslog" "log/syslog" ) @@ -321,7 +321,7 @@ could do: ```go import ( - log "github.com/Sirupsen/logrus" + log "github.com/sirupsen/logrus" ) init() { From 3454d74a4c8ac5c744770ebb3d61d8edd0e7b34d Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Fri, 12 May 2017 12:12:49 +0100 Subject: [PATCH 046/111] Enable ANSI colors on Windows 10 and newer --- terminal_windows.go | 48 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/terminal_windows.go b/terminal_windows.go index 05d2f91..8871857 100644 --- a/terminal_windows.go +++ b/terminal_windows.go @@ -8,8 +8,13 @@ package logrus import ( + "bytes" + "errors" "io" "os" + "os/exec" + "strconv" + "strings" "syscall" "unsafe" ) @@ -18,8 +23,51 @@ var kernel32 = syscall.NewLazyDLL("kernel32.dll") var ( procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procSetConsoleMode = kernel32.NewProc("SetConsoleMode") ) +const ( + enableProcessedOutput = 0x0001 + enableWrapAtEolOutput = 0x0002 + enableVirtualTerminalProcessing = 0x0004 +) + +func getVersion() (float64, error) { + stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{} + cmd := exec.Command("cmd", "ver") + cmd.Stdout = stdout + cmd.Stderr = stderr + err := cmd.Run() + if err != nil { + return -1, err + } + + version := strings.Replace(stdout.String(), "\n", "", -1) + version = strings.Replace(version, "\r\n", "", -1) + + x1 := strings.Index(version, "[Version") + + if x1 == -1 || strings.Index(version, "]") == -1 { + return -1, errors.New("Can't determine Windows version") + } + + return strconv.ParseFloat(version[x1+9:x1+13], 64) +} + +func init() { + ver, err := getVersion() + if err != nil { + panic(err) + } + + // Activate Virtual Processing for Windows CMD + // Info: https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx + if ver >= 10 { + handle := syscall.Handle(os.Stderr.Fd()) + procSetConsoleMode.Call(uintptr(handle), enableProcessedOutput|enableWrapAtEolOutput|enableVirtualTerminalProcessing) + } +} + // IsTerminal returns true if stderr's file descriptor is a terminal. func IsTerminal(f io.Writer) bool { switch v := f.(type) { From a06c2db727877572754835b0a159048fa6d1852a Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Fri, 12 May 2017 14:51:38 -0400 Subject: [PATCH 047/111] hooks: add a null logger for the lower-case import --- README.md | 21 +++++++---- hooks/null/null.go | 92 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 hooks/null/null.go diff --git a/README.md b/README.md index 65eec9c..08dd468 100644 --- a/README.md +++ b/README.md @@ -434,15 +434,22 @@ Logrus has a built in facility for asserting the presence of log messages. This * a test logger (`test.NewNullLogger`) that just records log messages (and does not output any): ```go -logger, hook := NewNullLogger() -logger.Error("Hello error") +import ( + "testing" + "github.com/sirupsen/logrus/hooks/null" +) -assert.Equal(1, len(hook.Entries)) -assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level) -assert.Equal("Hello error", hook.LastEntry().Message) +func TestSomething(t *testing.T) { + logger, hook := null.NewNullLogger() + logger.Error("Hello error") -hook.Reset() -assert.Nil(hook.LastEntry()) + assert.Equal(1, len(hook.Entries)) + assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level) + assert.Equal("Hello error", hook.LastEntry().Message) + + hook.Reset() + assert.Nil(hook.LastEntry()) +} ``` #### Fatal handlers diff --git a/hooks/null/null.go b/hooks/null/null.go new file mode 100644 index 0000000..01e9abc --- /dev/null +++ b/hooks/null/null.go @@ -0,0 +1,92 @@ +package null + +import ( + "io/ioutil" + "sync" + + "github.com/sirupsen/logrus" +) + +// Hook is a hook designed for dealing with logs in test scenarios. +type Hook struct { + // Entries is an array of all entries that have been received by this hook. + // For safe access, use the AllEntries() method, rather than reading this + // value directly. + Entries []*logrus.Entry + mu sync.RWMutex +} + +// NewGlobal installs a test hook for the global logger. +func NewGlobal() *Hook { + + hook := new(Hook) + logrus.AddHook(hook) + + return hook + +} + +// NewLocal installs a test hook for a given local logger. +func NewLocal(logger *logrus.Logger) *Hook { + + hook := new(Hook) + logger.Hooks.Add(hook) + + return hook + +} + +// NewNullLogger creates a discarding logger and installs the test hook. +func NewNullLogger() (*logrus.Logger, *Hook) { + + logger := logrus.New() + logger.Out = ioutil.Discard + + return logger, NewLocal(logger) + +} + +func (t *Hook) Fire(e *logrus.Entry) error { + t.mu.Lock() + defer t.mu.Unlock() + t.Entries = append(t.Entries, e) + return nil +} + +func (t *Hook) Levels() []logrus.Level { + return logrus.AllLevels +} + +// LastEntry returns the last entry that was logged or nil. +func (t *Hook) LastEntry() *logrus.Entry { + t.mu.RLock() + defer t.mu.RUnlock() + i := len(t.Entries) - 1 + if i < 0 { + return nil + } + // Make a copy, for safety + e := *t.Entries[i] + return &e +} + +// AllEntries returns all entries that were logged. +func (t *Hook) AllEntries() []*logrus.Entry { + t.mu.RLock() + defer t.mu.RUnlock() + // Make a copy so the returned value won't race with future log requests + entries := make([]*logrus.Entry, len(t.Entries)) + for i, entry := range t.Entries { + // Make a copy, for safety + e := *entry + entries[i] = &e + } + return entries +} + +// Reset removes all Entries from this test hook. +func (t *Hook) Reset() { + t.mu.Lock() + defer t.mu.Unlock() + t.Entries = make([]*logrus.Entry, 0) +} From a9ab54b9d5f3b2e945f9eda26ef0759f3ba3a46e Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Fri, 12 May 2017 15:09:01 -0400 Subject: [PATCH 048/111] readme: add note on casing --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 08dd468..b3c75cf 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,12 @@ time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x20822 exit status 1 ``` +#### Case-sensitivity + +The organization's name was changed to lower-case--and this will not be changed +back. If you are getting import conflicts due to case sensitivity, please use +the lower-case import: `github.com/sirupsen/logrus`. + #### Example The simplest way to use Logrus is simply the package-level exported logger: From f1444e62a8e2fad7dfded2086fc7a3015b09df70 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Fri, 12 May 2017 15:15:51 -0400 Subject: [PATCH 049/111] hooks: add null logger test --- README.md | 18 ++++++++++-------- hooks/null/null_test.go | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 hooks/null/null_test.go diff --git a/README.md b/README.md index b3c75cf..46dda85 100644 --- a/README.md +++ b/README.md @@ -440,21 +440,23 @@ Logrus has a built in facility for asserting the presence of log messages. This * a test logger (`test.NewNullLogger`) that just records log messages (and does not output any): ```go -import ( - "testing" +import( + "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/null" + "github.com/stretchr/testify/assert" + "testing" ) -func TestSomething(t *testing.T) { +func TestSomething(t*testing.T){ logger, hook := null.NewNullLogger() - logger.Error("Hello error") + logger.Error("Helloerror") - assert.Equal(1, len(hook.Entries)) - assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level) - assert.Equal("Hello error", hook.LastEntry().Message) + assert.Equal(t, 1, len(hook.Entries)) + assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level) + assert.Equal(t, "Helloerror", hook.LastEntry().Message) hook.Reset() - assert.Nil(hook.LastEntry()) + assert.Nil(t, hook.LastEntry()) } ``` diff --git a/hooks/null/null_test.go b/hooks/null/null_test.go new file mode 100644 index 0000000..10e8413 --- /dev/null +++ b/hooks/null/null_test.go @@ -0,0 +1,19 @@ +package null + +import ( + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestNullLogger(t *testing.T) { + logger, hook := NewNullLogger() + logger.Error("Helloerror") + + assert.Equal(t, 1, len(hook.Entries)) + assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level) + assert.Equal(t, "Helloerror", hook.LastEntry().Message) + + hook.Reset() + assert.Nil(t, hook.LastEntry()) +} From acfabf31db8f45a9174f54a0d48ea4d15627af4d Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Fri, 12 May 2017 15:21:58 -0400 Subject: [PATCH 050/111] logrus: use lower-case in comments and examples --- doc.go | 4 ++-- examples/basic/basic.go | 2 +- examples/hook/hook.go | 2 +- hooks/syslog/README.md | 2 +- hooks/test/test.go | 3 +++ json_formatter.go | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/doc.go b/doc.go index dddd5f8..da67aba 100644 --- a/doc.go +++ b/doc.go @@ -7,7 +7,7 @@ The simplest way to use Logrus is simply the package-level exported logger: package main import ( - log "github.com/Sirupsen/logrus" + log "github.com/sirupsen/logrus" ) func main() { @@ -21,6 +21,6 @@ The simplest way to use Logrus is simply the package-level exported logger: Output: time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10 -For a full guide visit https://github.com/Sirupsen/logrus +For a full guide visit https://github.com/sirupsen/logrus */ package logrus diff --git a/examples/basic/basic.go b/examples/basic/basic.go index ad703fc..3e112b4 100644 --- a/examples/basic/basic.go +++ b/examples/basic/basic.go @@ -1,7 +1,7 @@ package main import ( - "github.com/Sirupsen/logrus" + "github.com/sirupsen/logrus" // "os" ) diff --git a/examples/hook/hook.go b/examples/hook/hook.go index 3187f6d..c8470c3 100644 --- a/examples/hook/hook.go +++ b/examples/hook/hook.go @@ -1,7 +1,7 @@ package main import ( - "github.com/Sirupsen/logrus" + "github.com/sirupsen/logrus" "gopkg.in/gemnasium/logrus-airbrake-hook.v2" ) diff --git a/hooks/syslog/README.md b/hooks/syslog/README.md index 066704b..b9c509b 100644 --- a/hooks/syslog/README.md +++ b/hooks/syslog/README.md @@ -36,4 +36,4 @@ func main() { log.Hooks.Add(hook) } } -``` \ No newline at end of file +``` diff --git a/hooks/test/test.go b/hooks/test/test.go index a94a6f9..4eabccf 100644 --- a/hooks/test/test.go +++ b/hooks/test/test.go @@ -1,3 +1,6 @@ +// The Test package is used for testing logrus. It is here for backwards +// compatibility from when logrus' organization was upper-case. Please use +// lower-case logrus and the `null` package instead of this one. package test import ( diff --git a/json_formatter.go b/json_formatter.go index 753d6a2..e787ea1 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -47,7 +47,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { switch v := v.(type) { case error: // Otherwise errors are ignored by `encoding/json` - // https://github.com/Sirupsen/logrus/issues/137 + // https://github.com/sirupsen/logrus/issues/137 data[k] = v.Error() default: data[k] = v From 532c891a8976a5f9812031f01dfd26992bf08707 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Fri, 12 May 2017 22:28:26 +0100 Subject: [PATCH 051/111] Update terminal_windows.go --- terminal_windows.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/terminal_windows.go b/terminal_windows.go index 8871857..7a33630 100644 --- a/terminal_windows.go +++ b/terminal_windows.go @@ -41,7 +41,8 @@ func getVersion() (float64, error) { if err != nil { return -1, err } - + + // The output should be like "Microsoft Windows [Version XX.X.XXXXXX]" version := strings.Replace(stdout.String(), "\n", "", -1) version = strings.Replace(version, "\r\n", "", -1) @@ -57,7 +58,7 @@ func getVersion() (float64, error) { func init() { ver, err := getVersion() if err != nil { - panic(err) + return } // Activate Virtual Processing for Windows CMD From df6d5a71153f75a1192903835c8217b146f6de1a Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 15 May 2017 06:31:03 -0400 Subject: [PATCH 052/111] travis: don't go get --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ef00c00..db83e5a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,4 @@ go: - 1.7.x - 1.8.x - tip -install: - - go get -t ./... script: GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v ./... From 0efc3b9c6f225cbefcd4bd77c471221637c91223 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 15 May 2017 06:34:03 -0400 Subject: [PATCH 053/111] travis: don't go get recursively --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index db83e5a..a8a5240 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,4 +4,5 @@ go: - 1.7.x - 1.8.x - tip +install: go get -v script: GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v ./... From 5e5dc898656f695e2a086b8e12559febbfc01562 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 15 May 2017 06:45:16 -0400 Subject: [PATCH 054/111] travis: clean up --- .travis.yml | 9 +++++++-- README.md | 2 +- alt_exit_test.go | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index a8a5240..924f3c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,5 +4,10 @@ go: - 1.7.x - 1.8.x - tip -install: go get -v -script: GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v ./... +env: + - GOMAXPROCS=4 GORACE=halt_on_error=1 +install: + - go get github.com/stretchr/testify/assert +script: + - go test -race -v . + - cd hooks/null && go test -race -v . diff --git a/README.md b/README.md index b761d2f..2674b8b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Logrus :walrus: [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/Sirupsen/logrus?status.svg)](https://godoc.org/github.com/Sirupsen/logrus) +# Logrus :walrus: [![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/sirupsen/logrus?status.svg)](https://godoc.org/github.com/sirupsen/logrus) **Seeing weird case-sensitive problems?** See [this issue](https://github.com/sirupsen/logrus/issues/451#issuecomment-264332021). diff --git a/alt_exit_test.go b/alt_exit_test.go index 022b778..d182963 100644 --- a/alt_exit_test.go +++ b/alt_exit_test.go @@ -44,7 +44,7 @@ var testprog = []byte(` package main import ( - "github.com/Sirupsen/logrus" + "github.com/sirupsen/logrus" "flag" "fmt" "io/ioutil" From c37067a498dc26574251f8499c3d8f6c60168b01 Mon Sep 17 00:00:00 2001 From: Shulhan Date: Thu, 18 May 2017 17:03:42 +0700 Subject: [PATCH 055/111] README: add hook for Mattermost --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2674b8b..6ea96f3 100644 --- a/README.md +++ b/README.md @@ -261,6 +261,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) | | [Logstash](https://github.com/bshuster-repo/logrus-logstash-hook) | Hook for logging to [Logstash](https://www.elastic.co/products/logstash) | | [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail | +| [Mattermost](https://github.com/shuLhan/mattermost-integration/tree/master/hooks/logrus) | Hook for logging to [Mattermost](https://mattermost.com/) | | [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb | | [NATS-Hook](https://github.com/rybit/nats_logrus_hook) | Hook for logging to [NATS](https://nats.io) | | [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit | From 0afea37159ef443ea0ca20dbd45ad7006b95edfe Mon Sep 17 00:00:00 2001 From: Tomas Doran Date: Fri, 19 May 2017 13:00:16 +0100 Subject: [PATCH 056/111] Update due to rename of github account --- hooks/syslog/README.md | 8 ++++---- hooks/syslog/syslog.go | 2 +- hooks/syslog/syslog_test.go | 2 +- hooks/test/test.go | 2 +- hooks/test/test_test.go | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hooks/syslog/README.md b/hooks/syslog/README.md index b9c509b..92b391c 100644 --- a/hooks/syslog/README.md +++ b/hooks/syslog/README.md @@ -5,8 +5,8 @@ ```go import ( "log/syslog" - "github.com/Sirupsen/logrus" - logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" + "github.com/sirupsen/logrus" + logrus_syslog "github.com/sirupsen/logrus/hooks/syslog" ) func main() { @@ -24,8 +24,8 @@ If you want to connect to local syslog (Ex. "/dev/log" or "/var/run/syslog" or " ```go import ( "log/syslog" - "github.com/Sirupsen/logrus" - logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" + "github.com/sirupsen/logrus" + logrus_syslog "github.com/sirupsen/logrus/hooks/syslog" ) func main() { diff --git a/hooks/syslog/syslog.go b/hooks/syslog/syslog.go index a36e200..204f001 100644 --- a/hooks/syslog/syslog.go +++ b/hooks/syslog/syslog.go @@ -4,7 +4,7 @@ package logrus_syslog import ( "fmt" - "github.com/Sirupsen/logrus" + "github.com/sirupsen/logrus" "log/syslog" "os" ) diff --git a/hooks/syslog/syslog_test.go b/hooks/syslog/syslog_test.go index 42762dc..8d7fbe4 100644 --- a/hooks/syslog/syslog_test.go +++ b/hooks/syslog/syslog_test.go @@ -1,7 +1,7 @@ package logrus_syslog import ( - "github.com/Sirupsen/logrus" + "github.com/sirupsen/logrus" "log/syslog" "testing" ) diff --git a/hooks/test/test.go b/hooks/test/test.go index 4eabccf..62c4845 100644 --- a/hooks/test/test.go +++ b/hooks/test/test.go @@ -7,7 +7,7 @@ import ( "io/ioutil" "sync" - "github.com/Sirupsen/logrus" + "github.com/sirupsen/logrus" ) // Hook is a hook designed for dealing with logs in test scenarios. diff --git a/hooks/test/test_test.go b/hooks/test/test_test.go index d69455b..3f55cfe 100644 --- a/hooks/test/test_test.go +++ b/hooks/test/test_test.go @@ -3,7 +3,7 @@ package test import ( "testing" - "github.com/Sirupsen/logrus" + "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" ) From a62b1531c0b6f08944e96850e76a95a1dd6e8f9b Mon Sep 17 00:00:00 2001 From: Etourneau Gwenn Date: Thu, 25 May 2017 09:53:41 +0900 Subject: [PATCH 057/111] Updating Readme to add syslogtls repo. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2674b8b..725b10b 100644 --- a/README.md +++ b/README.md @@ -276,6 +276,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [Stackdriver](https://github.com/knq/sdhook) | Hook for logging to [Google Stackdriver](https://cloud.google.com/logging/) | | [Sumorus](https://github.com/doublefree/sumorus) | Hook for logging to [SumoLogic](https://www.sumologic.com/)| | [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. | +| [Syslog TLS](https://github.com/shinji62/logrus-syslog-ng) | Send errors to remote syslog server with TLS support. | | [TraceView](https://github.com/evalphobia/logrus_appneta) | Hook for logging to [AppNeta TraceView](https://www.appneta.com/products/traceview/) | | [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) | | [logz.io](https://github.com/ripcurld00d/logrus-logzio-hook) | Hook for logging to [logz.io](https://logz.io), a Log as a Service using Logstash | From d3731ac0267e2871cc904a79efb9e5f52f0b48b2 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 6 Jun 2017 15:05:30 -0400 Subject: [PATCH 058/111] readme: more explicit case instructions --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2674b8b..de7bff0 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,5 @@ # Logrus :walrus: [![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/sirupsen/logrus?status.svg)](https://godoc.org/github.com/sirupsen/logrus) -**Seeing weird case-sensitive problems?** See [this -issue](https://github.com/sirupsen/logrus/issues/451#issuecomment-264332021). -This change has been reverted. I apologize for causing this. I greatly -underestimated the impact this would have. Logrus strives for stability and -backwards compatibility and failed to provide that. - Logrus is a structured logger for Go (golang), completely API compatible with the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not yet stable (pre 1.0). Logrus itself is completely stable and has been used in @@ -13,6 +7,16 @@ many large deployments. The core API is unlikely to change much but please version control your Logrus to make sure you aren't fetching latest `master` on every build.** +**Seeing weird case-sensitive problems?** Unfortunately, the author failed to +realize the consequences of renaming to lower-case. Due to the Go package +environment, this caused issues. Regretfully, there's no turning back now. +Everything using `logrus` will need to use the lower-case: +`github.com/sirupsen/logrus`. Any package that isn't, should be changed. + +I am terribly sorry for this inconvenience. Logrus strives hard for backwards +compatibility, and the author failed to realize the cascading consequences of +such a name-change. + Nicely color-coded in development (when a TTY is attached, otherwise just plain text): From abb8cd67b69d9f376b419d4c1f728107316506f8 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 6 Jun 2017 16:59:16 -0400 Subject: [PATCH 059/111] readme: update with reference to fixing glide --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b4994a..cbe8b69 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ Everything using `logrus` will need to use the lower-case: I am terribly sorry for this inconvenience. Logrus strives hard for backwards compatibility, and the author failed to realize the cascading consequences of -such a name-change. +such a name-change. To fix Glide, see [these +comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437). Nicely color-coded in development (when a TTY is attached, otherwise just plain text): From 68cec9f21fbf3ea8d8f98c044bc6ce05f17b267a Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 6 Jun 2017 16:59:45 -0400 Subject: [PATCH 060/111] hooks: remove non-released null hook --- hooks/null/null.go | 92 ----------------------------------------- hooks/null/null_test.go | 19 --------- 2 files changed, 111 deletions(-) delete mode 100644 hooks/null/null.go delete mode 100644 hooks/null/null_test.go diff --git a/hooks/null/null.go b/hooks/null/null.go deleted file mode 100644 index 01e9abc..0000000 --- a/hooks/null/null.go +++ /dev/null @@ -1,92 +0,0 @@ -package null - -import ( - "io/ioutil" - "sync" - - "github.com/sirupsen/logrus" -) - -// Hook is a hook designed for dealing with logs in test scenarios. -type Hook struct { - // Entries is an array of all entries that have been received by this hook. - // For safe access, use the AllEntries() method, rather than reading this - // value directly. - Entries []*logrus.Entry - mu sync.RWMutex -} - -// NewGlobal installs a test hook for the global logger. -func NewGlobal() *Hook { - - hook := new(Hook) - logrus.AddHook(hook) - - return hook - -} - -// NewLocal installs a test hook for a given local logger. -func NewLocal(logger *logrus.Logger) *Hook { - - hook := new(Hook) - logger.Hooks.Add(hook) - - return hook - -} - -// NewNullLogger creates a discarding logger and installs the test hook. -func NewNullLogger() (*logrus.Logger, *Hook) { - - logger := logrus.New() - logger.Out = ioutil.Discard - - return logger, NewLocal(logger) - -} - -func (t *Hook) Fire(e *logrus.Entry) error { - t.mu.Lock() - defer t.mu.Unlock() - t.Entries = append(t.Entries, e) - return nil -} - -func (t *Hook) Levels() []logrus.Level { - return logrus.AllLevels -} - -// LastEntry returns the last entry that was logged or nil. -func (t *Hook) LastEntry() *logrus.Entry { - t.mu.RLock() - defer t.mu.RUnlock() - i := len(t.Entries) - 1 - if i < 0 { - return nil - } - // Make a copy, for safety - e := *t.Entries[i] - return &e -} - -// AllEntries returns all entries that were logged. -func (t *Hook) AllEntries() []*logrus.Entry { - t.mu.RLock() - defer t.mu.RUnlock() - // Make a copy so the returned value won't race with future log requests - entries := make([]*logrus.Entry, len(t.Entries)) - for i, entry := range t.Entries { - // Make a copy, for safety - e := *entry - entries[i] = &e - } - return entries -} - -// Reset removes all Entries from this test hook. -func (t *Hook) Reset() { - t.mu.Lock() - defer t.mu.Unlock() - t.Entries = make([]*logrus.Entry, 0) -} diff --git a/hooks/null/null_test.go b/hooks/null/null_test.go deleted file mode 100644 index 10e8413..0000000 --- a/hooks/null/null_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package null - -import ( - "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" - "testing" -) - -func TestNullLogger(t *testing.T) { - logger, hook := NewNullLogger() - logger.Error("Helloerror") - - assert.Equal(t, 1, len(hook.Entries)) - assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level) - assert.Equal(t, "Helloerror", hook.LastEntry().Message) - - hook.Reset() - assert.Nil(t, hook.LastEntry()) -} From 202f25545ea4cf9b191ff7f846df5d87c9382c2b Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 8 Jun 2017 17:02:02 -0400 Subject: [PATCH 061/111] changelog: bump to v1.0.0 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 747e4d8..63d415e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 1.0.0 + +* Officially changed name to lower-case +* bug: colors on Windows 10 (#541) +* bug: fix race in accessing level (#512) + # 0.11.5 * feature: add writer and writerlevel to entry (#372) From 85b1699d505667d13f8ac4478c1debbf85d6c5de Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 8 Jun 2017 18:14:41 -0400 Subject: [PATCH 062/111] ci: v1.0.0 fix travis --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 924f3c4..ceace13 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,4 +10,3 @@ install: - go get github.com/stretchr/testify/assert script: - go test -race -v . - - cd hooks/null && go test -race -v . From a279ebafa6aef6a9d62e51ba02f397316e6f1838 Mon Sep 17 00:00:00 2001 From: Alex Haynes Date: Tue, 13 Jun 2017 13:28:47 -0500 Subject: [PATCH 063/111] Update hook name There is no hooks/null --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cbe8b69..ed03178 100644 --- a/README.md +++ b/README.md @@ -449,13 +449,13 @@ Logrus has a built in facility for asserting the presence of log messages. This ```go import( "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/null" + "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/assert" "testing" ) func TestSomething(t*testing.T){ - logger, hook := null.NewNullLogger() + logger, hook := test.NewNullLogger() logger.Error("Helloerror") assert.Equal(t, 1, len(hook.Entries)) From 5d674288571c9801c253e913d8d49c8e79230d40 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 18 Jun 2017 09:48:52 -0400 Subject: [PATCH 064/111] Allow more chars in unquoted text formatter output --- text_formatter.go | 2 +- text_formatter_test.go | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index ba88854..6821fcb 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -153,7 +153,7 @@ func (f *TextFormatter) needsQuoting(text string) bool { if !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || - ch == '-' || ch == '.') { + ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') { return true } } diff --git a/text_formatter_test.go b/text_formatter_test.go index 9793b5f..08571db 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -28,7 +28,13 @@ func TestQuoting(t *testing.T) { checkQuoting(false, "abcd") checkQuoting(false, "v1.0") checkQuoting(false, "1234567890") - checkQuoting(true, "/foobar") + checkQuoting(false, "/foobar") + checkQuoting(false, "foo_bar") + checkQuoting(false, "foo@bar") + checkQuoting(false, "foobar^") + checkQuoting(false, "+/-_^@f.oobar") + checkQuoting(true, "foobar$") + checkQuoting(true, "&foobar") checkQuoting(true, "x y") checkQuoting(true, "x,y") checkQuoting(false, errors.New("invalid")) @@ -38,7 +44,12 @@ func TestQuoting(t *testing.T) { tf.QuoteCharacter = "`" checkQuoting(false, "") checkQuoting(false, "abcd") - checkQuoting(true, "/foobar") + checkQuoting(false, "/foobar") + checkQuoting(false, "foo_bar") + checkQuoting(false, "foo@bar") + checkQuoting(false, "foobar^") + checkQuoting(true, "foobar$") + checkQuoting(true, "&foobar") checkQuoting(true, errors.New("invalid argument")) // Test for multi-character quotes. From 3d4380f53a34dcdc95f0c1db702615992b38d9a4 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 20 Jun 2017 10:45:10 -0400 Subject: [PATCH 065/111] readme: remove < 1.0.0 note from readme --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index cbe8b69..3f324e2 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,7 @@ # Logrus :walrus: [![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/sirupsen/logrus?status.svg)](https://godoc.org/github.com/sirupsen/logrus) Logrus is a structured logger for Go (golang), completely API compatible with -the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not -yet stable (pre 1.0). Logrus itself is completely stable and has been used in -many large deployments. The core API is unlikely to change much but please -version control your Logrus to make sure you aren't fetching latest `master` on -every build.** +the standard library logger. [Godoc][godoc]. **Seeing weird case-sensitive problems?** Unfortunately, the author failed to realize the consequences of renaming to lower-case. Due to the Go package From 7dd06bf38e1e13df288d471a57d5adbac106be9e Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 29 Jun 2017 20:54:20 -0400 Subject: [PATCH 066/111] readme: clarify casing issues further --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3f324e2..7941ebd 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,14 @@ Logrus is a structured logger for Go (golang), completely API compatible with the standard library logger. [Godoc][godoc]. -**Seeing weird case-sensitive problems?** Unfortunately, the author failed to -realize the consequences of renaming to lower-case. Due to the Go package -environment, this caused issues. Regretfully, there's no turning back now. +**Seeing weird case-sensitive problems?** It's in the past been possible to +import Logrus as both upper- and lower-case. Due to the Go package environment, +this caused issues in the community and we needed a standard. Some environments +experienced problems with the upper-case variant, so the lower-case was decided. Everything using `logrus` will need to use the lower-case: `github.com/sirupsen/logrus`. Any package that isn't, should be changed. -I am terribly sorry for this inconvenience. Logrus strives hard for backwards +**I am terribly sorry for this inconvenience.** Logrus strives hard for backwards compatibility, and the author failed to realize the cascading consequences of such a name-change. To fix Glide, see [these comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437). From 59d0ca41e5faad81cac03f7a7d84ba80d9cc9673 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 6 Jul 2017 09:44:07 -0400 Subject: [PATCH 067/111] readme: add note about lookign for maintainers --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 7941ebd..f42b652 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,11 @@ compatibility, and the author failed to realize the cascading consequences of such a name-change. To fix Glide, see [these comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437). +**Are you interested in assisting in maintaining Logrus?** Currently I have a +lot of obligations, and I am unable to provide Logrus with the maintainership it +needs. If you'd like to help, please reach out to me at `simon at author's +username dot com`. + Nicely color-coded in development (when a TTY is attached, otherwise just plain text): From 3963c935b8b238b36dd7d31d8a430c139a6b7251 Mon Sep 17 00:00:00 2001 From: Andrew Pennebaker Date: Thu, 6 Jul 2017 17:21:34 -0500 Subject: [PATCH 068/111] fix casing fix casing leftover from older logrus --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f42b652..018e915 100644 --- a/README.md +++ b/README.md @@ -282,7 +282,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. | | [Stackdriver](https://github.com/knq/sdhook) | Hook for logging to [Google Stackdriver](https://cloud.google.com/logging/) | | [Sumorus](https://github.com/doublefree/sumorus) | Hook for logging to [SumoLogic](https://www.sumologic.com/)| -| [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. | +| [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. | | [Syslog TLS](https://github.com/shinji62/logrus-syslog-ng) | Send errors to remote syslog server with TLS support. | | [TraceView](https://github.com/evalphobia/logrus_appneta) | Hook for logging to [AppNeta TraceView](https://www.appneta.com/products/traceview/) | | [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) | From 20d755ea5e94d92e979f3fbb699844465a1a0530 Mon Sep 17 00:00:00 2001 From: Steve Jarvis Date: Fri, 7 Jul 2017 16:29:37 -0400 Subject: [PATCH 069/111] Export logger's set level function to address #503. --- exported.go | 2 +- logger.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exported.go b/exported.go index 1aeaa90..013183e 100644 --- a/exported.go +++ b/exported.go @@ -31,7 +31,7 @@ func SetFormatter(formatter Formatter) { func SetLevel(level Level) { std.mu.Lock() defer std.mu.Unlock() - std.setLevel(level) + std.SetLevel(level) } // GetLevel returns the standard logger level. diff --git a/logger.go b/logger.go index 370fff5..b44966f 100644 --- a/logger.go +++ b/logger.go @@ -312,6 +312,6 @@ func (logger *Logger) level() Level { return Level(atomic.LoadUint32((*uint32)(&logger.Level))) } -func (logger *Logger) setLevel(level Level) { +func (logger *Logger) SetLevel(level Level) { atomic.StoreUint32((*uint32)(&logger.Level), uint32(level)) } From f78f8d07f6aca1903611d049bf064c8cea107d9d Mon Sep 17 00:00:00 2001 From: kpcyrd Date: Mon, 24 Apr 2017 19:27:50 +0200 Subject: [PATCH 070/111] Safely format data when printing Fixes #531 --- text_formatter.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index ba88854..8006c0d 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -174,14 +174,14 @@ func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { if !f.needsQuoting(value) { b.WriteString(value) } else { - fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, value, f.QuoteCharacter) + fmt.Fprintf(b, "%q", value) } case error: errmsg := value.Error() if !f.needsQuoting(errmsg) { b.WriteString(errmsg) } else { - fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, errmsg, f.QuoteCharacter) + fmt.Fprintf(b, "%q", errmsg) } default: fmt.Fprint(b, value) From 0383f49850e27656e3befd06dfaef6d224a94276 Mon Sep 17 00:00:00 2001 From: Paul Seiffert Date: Mon, 10 Jul 2017 14:09:37 +0200 Subject: [PATCH 071/111] Use custom quote char and escape it --- text_formatter.go | 10 ++++++++-- text_formatter_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index 8006c0d..32adbf8 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -174,14 +174,20 @@ func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { if !f.needsQuoting(value) { b.WriteString(value) } else { - fmt.Fprintf(b, "%q", value) + escapedQuote := fmt.Sprintf("\\%s", f.QuoteCharacter) + escapedValue := strings.Replace(value, f.QuoteCharacter, escapedQuote, -1) + + fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, escapedValue, f.QuoteCharacter) } case error: errmsg := value.Error() if !f.needsQuoting(errmsg) { b.WriteString(errmsg) } else { - fmt.Fprintf(b, "%q", errmsg) + escapedQuote := fmt.Sprintf("\\%s", f.QuoteCharacter) + escapedErrmsg := strings.Replace(errmsg, f.QuoteCharacter, escapedQuote, -1) + + fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, escapedErrmsg, f.QuoteCharacter) } default: fmt.Fprint(b, value) diff --git a/text_formatter_test.go b/text_formatter_test.go index 9793b5f..f02dc0c 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -53,6 +53,48 @@ func TestQuoting(t *testing.T) { checkQuoting(true, errors.New("invalid argument")) } +func TestEscaping_DefaultQuoteCharacter(t *testing.T) { + tf := &TextFormatter{DisableColors: true} + + testCases := []struct { + value string + expected string + }{ + {`ba"r`, `ba\"r`}, + {`ba'r`, `ba'r`}, + } + + for _, tc := range testCases { + b, _ := tf.Format(WithField("test", tc.value)) + if !bytes.Contains(b, []byte(tc.expected)) { + t.Errorf("escaping expected for %q (result was %q instead of %q)", tc.value, string(b), tc.expected) + } + } +} + +func TestEscaping_CustomQuoteCharacter(t *testing.T) { + tf := &TextFormatter{DisableColors: true} + + testCases := []struct { + value string + expected string + quoteChar string + }{ + {`ba"r`, `ba"r`, `'`}, + {`ba'r`, `ba\'r`, `'`}, + {`ba^r`, `ba\^r`, `^`}, + {`ba'r`, `ba'r`, `^`}, + } + + for _, tc := range testCases { + tf.QuoteCharacter = tc.quoteChar + b, _ := tf.Format(WithField("test", tc.value)) + if !bytes.Contains(b, []byte(tc.expected)) { + t.Errorf("escaping expected for %q (result was %q instead of %q)", tc.value, string(b), tc.expected) + } + } +} + func TestTimestampFormat(t *testing.T) { checkTimeStr := func(format string) { customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format} From 0025402362434acb5a02999b920080debe8bba4f Mon Sep 17 00:00:00 2001 From: Paul Seiffert Date: Mon, 10 Jul 2017 14:17:59 +0200 Subject: [PATCH 072/111] Extract quoting into separate method --- text_formatter.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index 32adbf8..26dcc15 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -174,22 +174,23 @@ func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { if !f.needsQuoting(value) { b.WriteString(value) } else { - escapedQuote := fmt.Sprintf("\\%s", f.QuoteCharacter) - escapedValue := strings.Replace(value, f.QuoteCharacter, escapedQuote, -1) - - fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, escapedValue, f.QuoteCharacter) + b.WriteString(f.quoteString(value)) } case error: errmsg := value.Error() if !f.needsQuoting(errmsg) { b.WriteString(errmsg) } else { - escapedQuote := fmt.Sprintf("\\%s", f.QuoteCharacter) - escapedErrmsg := strings.Replace(errmsg, f.QuoteCharacter, escapedQuote, -1) - - fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, escapedErrmsg, f.QuoteCharacter) + b.WriteString(f.quoteString(errmsg)) } default: fmt.Fprint(b, value) } } + +func (f *TextFormatter) quoteString(v string) string { + escapedQuote := fmt.Sprintf("\\%s", f.QuoteCharacter) + escapedValue := strings.Replace(v, f.QuoteCharacter, escapedQuote, -1) + + return fmt.Sprintf("%s%v%s", f.QuoteCharacter, escapedValue, f.QuoteCharacter) +} From 04a001ce508c6946033a16224759b3e462a57337 Mon Sep 17 00:00:00 2001 From: Paul Seiffert Date: Mon, 10 Jul 2017 17:40:03 +0200 Subject: [PATCH 073/111] Mention bugfix in changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63d415e..6336322 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.0.1 + +* bug: fix escaping in text formatter (#575) + # 1.0.0 * Officially changed name to lower-case From 7f976d3a76720c4c27af2ba716b85d2e0a7e38b1 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 10 Jul 2017 10:32:56 -0400 Subject: [PATCH 074/111] readme: link to comment on casing --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e479352..82aeb4e 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,10 @@ experienced problems with the upper-case variant, so the lower-case was decided. Everything using `logrus` will need to use the lower-case: `github.com/sirupsen/logrus`. Any package that isn't, should be changed. -**I am terribly sorry for this inconvenience.** Logrus strives hard for backwards -compatibility, and the author failed to realize the cascading consequences of -such a name-change. To fix Glide, see [these +To fix Glide, see [these comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437). +For an in-depth explanation of the casing issue, see [this +comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276). **Are you interested in assisting in maintaining Logrus?** Currently I have a lot of obligations, and I am unable to provide Logrus with the maintainership it From 10e5e38b53df0f5f80e72a929c01af6c84d7f847 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Wed, 12 Jul 2017 14:33:57 +0200 Subject: [PATCH 075/111] remove ^ from custom quote characters As of #563, this charater is not quoted anymore. --- text_formatter_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/text_formatter_test.go b/text_formatter_test.go index dc4911f..1fc55ae 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -93,7 +93,6 @@ func TestEscaping_CustomQuoteCharacter(t *testing.T) { }{ {`ba"r`, `ba"r`, `'`}, {`ba'r`, `ba\'r`, `'`}, - {`ba^r`, `ba\^r`, `^`}, {`ba'r`, `ba'r`, `^`}, } From b9cfd8264512c3534698605bc841c6b71521a69f Mon Sep 17 00:00:00 2001 From: Paul Seiffert Date: Wed, 12 Jul 2017 17:15:13 +0200 Subject: [PATCH 076/111] Quote non-string values if necessary --- text_formatter.go | 7 ++++++- text_formatter_test.go | 11 +++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/text_formatter.go b/text_formatter.go index e125015..6f573a2 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -184,7 +184,12 @@ func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { b.WriteString(f.quoteString(errmsg)) } default: - fmt.Fprint(b, value) + s := fmt.Sprint(value) + if !f.needsQuoting(s) { + b.WriteString(s) + } else { + b.WriteString(f.quoteString(s)) + } } } diff --git a/text_formatter_test.go b/text_formatter_test.go index 1fc55ae..93f47b7 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -6,6 +6,7 @@ import ( "strings" "testing" "time" + "fmt" ) func TestQuoting(t *testing.T) { @@ -83,6 +84,16 @@ func TestEscaping_DefaultQuoteCharacter(t *testing.T) { } } +func TestEscaping_Time(t *testing.T) { + tf := &TextFormatter{DisableColors: true} + ts := time.Now() + + b, _ := tf.Format(WithField("test", ts)) + if !bytes.Contains(b, []byte(fmt.Sprintf("\"%s\"", ts.Format("2006-01-02 15:04:05.999999999 -0700 MST")))) { + t.Errorf("escaping expected for %q (result was %q)", ts, string(b)) + } +} + func TestEscaping_CustomQuoteCharacter(t *testing.T) { tf := &TextFormatter{DisableColors: true} From 4c4851c96ab0aefe478354f1cbce49060b3c474f Mon Sep 17 00:00:00 2001 From: Paul Seiffert Date: Wed, 12 Jul 2017 17:16:13 +0200 Subject: [PATCH 077/111] Reduce duplicate code --- text_formatter.go | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index 6f573a2..6fe24fc 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -169,27 +169,21 @@ func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interf } func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { + var stringVal string + switch value := value.(type) { case string: - if !f.needsQuoting(value) { - b.WriteString(value) - } else { - b.WriteString(f.quoteString(value)) - } + stringVal = value case error: - errmsg := value.Error() - if !f.needsQuoting(errmsg) { - b.WriteString(errmsg) - } else { - b.WriteString(f.quoteString(errmsg)) - } + stringVal = value.Error() default: - s := fmt.Sprint(value) - if !f.needsQuoting(s) { - b.WriteString(s) - } else { - b.WriteString(f.quoteString(s)) - } + stringVal = fmt.Sprint(value) + } + + if !f.needsQuoting(stringVal) { + b.WriteString(stringVal) + } else { + b.WriteString(f.quoteString(stringVal)) } } From 5f89343f84f01bd781cfc680d045148ea0a6d178 Mon Sep 17 00:00:00 2001 From: Paul Seiffert Date: Wed, 12 Jul 2017 17:29:56 +0200 Subject: [PATCH 078/111] Generalize test case --- text_formatter_test.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/text_formatter_test.go b/text_formatter_test.go index 93f47b7..d7b3bcb 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -84,13 +84,24 @@ func TestEscaping_DefaultQuoteCharacter(t *testing.T) { } } -func TestEscaping_Time(t *testing.T) { +func TestEscaping_Interface(t *testing.T) { tf := &TextFormatter{DisableColors: true} + ts := time.Now() - b, _ := tf.Format(WithField("test", ts)) - if !bytes.Contains(b, []byte(fmt.Sprintf("\"%s\"", ts.Format("2006-01-02 15:04:05.999999999 -0700 MST")))) { - t.Errorf("escaping expected for %q (result was %q)", ts, string(b)) + testCases := []struct { + value interface{} + expected string + }{ + {ts, fmt.Sprintf("\"%s\"", ts.String())}, + {errors.New("error: something went wrong"), "\"error: something went wrong\""}, + } + + for _, tc := range testCases { + b, _ := tf.Format(WithField("test", tc.value)) + if !bytes.Contains(b, []byte(tc.expected)) { + t.Errorf("escaping expected for %q (result was %q instead of %q)", tc.value, string(b), tc.expected) + } } } From b019ab48c544b5429f6c2f2908a5f86f235daefb Mon Sep 17 00:00:00 2001 From: Paul Seiffert Date: Wed, 12 Jul 2017 17:33:04 +0200 Subject: [PATCH 079/111] Error is not a special case anymore --- text_formatter.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index 6fe24fc..6aa48cf 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -169,14 +169,8 @@ func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interf } func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { - var stringVal string - - switch value := value.(type) { - case string: - stringVal = value - case error: - stringVal = value.Error() - default: + stringVal, ok := value.(string) + if !ok { stringVal = fmt.Sprint(value) } From 1fb89425423e0882d77d087f2a8f997e533a34a8 Mon Sep 17 00:00:00 2001 From: Paul Seiffert Date: Wed, 12 Jul 2017 18:16:53 +0200 Subject: [PATCH 080/111] Mention quoting bugfix in CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6336322..03fe4b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.0.2 + +* bug: quote non-string values in text formatter (#583) + # 1.0.1 * bug: fix escaping in text formatter (#575) From a3f95b5c423586578a4e099b11a46c2479628cac Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Thu, 13 Jul 2017 13:42:50 +0200 Subject: [PATCH 081/111] add changelog entry about making SetLevel public --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03fe4b1..c443aed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # 1.0.2 * bug: quote non-string values in text formatter (#583) +* Make (*Logger) SetLevel a public method # 1.0.1 From 2727ac94b0f8407edbcd02c91050921ef6417f42 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Thu, 13 Jul 2017 13:46:35 +0200 Subject: [PATCH 082/111] remove QuoteCharacter option This seems to be one of the most reported issues, as it makes it a lot harder to safely escape strings. This option is very much an edge case, and it's causing too much issues compared to what it provide. --- text_formatter.go | 16 +------------- text_formatter_test.go | 47 ++++-------------------------------------- 2 files changed, 5 insertions(+), 58 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index 6aa48cf..cf3f17f 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -52,10 +52,6 @@ type TextFormatter struct { // QuoteEmptyFields will wrap empty fields in quotes if true QuoteEmptyFields bool - // QuoteCharacter can be set to the override the default quoting character " - // with something else. For example: ', or `. - QuoteCharacter string - // Whether the logger's out is to a terminal isTerminal bool @@ -63,9 +59,6 @@ type TextFormatter struct { } func (f *TextFormatter) init(entry *Entry) { - if len(f.QuoteCharacter) == 0 { - f.QuoteCharacter = "\"" - } if entry.Logger != nil { f.isTerminal = IsTerminal(entry.Logger.Out) } @@ -177,13 +170,6 @@ func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { if !f.needsQuoting(stringVal) { b.WriteString(stringVal) } else { - b.WriteString(f.quoteString(stringVal)) + b.WriteString(fmt.Sprintf("%q", stringVal)) } } - -func (f *TextFormatter) quoteString(v string) string { - escapedQuote := fmt.Sprintf("\\%s", f.QuoteCharacter) - escapedValue := strings.Replace(v, f.QuoteCharacter, escapedQuote, -1) - - return fmt.Sprintf("%s%v%s", f.QuoteCharacter, escapedValue, f.QuoteCharacter) -} diff --git a/text_formatter_test.go b/text_formatter_test.go index d7b3bcb..ecb8f12 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -3,10 +3,10 @@ package logrus import ( "bytes" "errors" + "fmt" "strings" "testing" "time" - "fmt" ) func TestQuoting(t *testing.T) { @@ -15,7 +15,7 @@ func TestQuoting(t *testing.T) { checkQuoting := func(q bool, value interface{}) { b, _ := tf.Format(WithField("test", value)) idx := bytes.Index(b, ([]byte)("test=")) - cont := bytes.Contains(b[idx+5:], []byte(tf.QuoteCharacter)) + cont := bytes.Contains(b[idx+5:], []byte("\"")) if cont != q { if q { t.Errorf("quoting expected for: %#v", value) @@ -41,23 +41,6 @@ func TestQuoting(t *testing.T) { checkQuoting(false, errors.New("invalid")) checkQuoting(true, errors.New("invalid argument")) - // Test for custom quote character. - tf.QuoteCharacter = "`" - checkQuoting(false, "") - checkQuoting(false, "abcd") - checkQuoting(false, "/foobar") - checkQuoting(false, "foo_bar") - checkQuoting(false, "foo@bar") - checkQuoting(false, "foobar^") - checkQuoting(true, "foobar$") - checkQuoting(true, "&foobar") - checkQuoting(true, errors.New("invalid argument")) - - // Test for multi-character quotes. - tf.QuoteCharacter = "§~±" - checkQuoting(false, "abcd") - checkQuoting(true, errors.New("invalid argument")) - // Test for quoting empty fields. tf.QuoteEmptyFields = true checkQuoting(true, "") @@ -65,7 +48,7 @@ func TestQuoting(t *testing.T) { checkQuoting(true, errors.New("invalid argument")) } -func TestEscaping_DefaultQuoteCharacter(t *testing.T) { +func TestEscaping(t *testing.T) { tf := &TextFormatter{DisableColors: true} testCases := []struct { @@ -105,35 +88,13 @@ func TestEscaping_Interface(t *testing.T) { } } -func TestEscaping_CustomQuoteCharacter(t *testing.T) { - tf := &TextFormatter{DisableColors: true} - - testCases := []struct { - value string - expected string - quoteChar string - }{ - {`ba"r`, `ba"r`, `'`}, - {`ba'r`, `ba\'r`, `'`}, - {`ba'r`, `ba'r`, `^`}, - } - - for _, tc := range testCases { - tf.QuoteCharacter = tc.quoteChar - b, _ := tf.Format(WithField("test", tc.value)) - if !bytes.Contains(b, []byte(tc.expected)) { - t.Errorf("escaping expected for %q (result was %q instead of %q)", tc.value, string(b), tc.expected) - } - } -} - func TestTimestampFormat(t *testing.T) { checkTimeStr := func(format string) { customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format} customStr, _ := customFormatter.Format(WithField("test", "test")) timeStart := bytes.Index(customStr, ([]byte)("time=")) timeEnd := bytes.Index(customStr, ([]byte)("level=")) - timeStr := customStr[timeStart+5+len(customFormatter.QuoteCharacter) : timeEnd-1-len(customFormatter.QuoteCharacter)] + timeStr := customStr[timeStart+5+len("\"") : timeEnd-1-len("\"")] if format == "" { format = time.RFC3339 } From 95002bc71734b797b2dc8ffb20ffc725c9f543ed Mon Sep 17 00:00:00 2001 From: DmitriyMV Date: Fri, 14 Jul 2017 14:59:01 +0300 Subject: [PATCH 083/111] Improve logrus.Entry.Level documentation --- entry.go | 1 + 1 file changed, 1 insertion(+) diff --git a/entry.go b/entry.go index 320e5d5..5bf582e 100644 --- a/entry.go +++ b/entry.go @@ -35,6 +35,7 @@ type Entry struct { Time time.Time // Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic + // This field will be set on entry firing and the value will be equal to the one in Logger struct field. Level Level // Message passed to Debug, Info, Warn, Error, Fatal or Panic From f30ff25fb7e208f9fe65f4ae913129b8220d30c1 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Tue, 18 Jul 2017 15:18:46 +0200 Subject: [PATCH 084/111] rename logrus_syslog package to syslog Package names shouldn't be using underscores. There is also nothing wrong with naming this package `syslog`. People can include it with any name they wish. --- hooks/syslog/README.md | 8 ++++---- hooks/syslog/syslog.go | 5 +++-- hooks/syslog/syslog_test.go | 5 +++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/hooks/syslog/README.md b/hooks/syslog/README.md index 92b391c..1bbc0f7 100644 --- a/hooks/syslog/README.md +++ b/hooks/syslog/README.md @@ -6,12 +6,12 @@ import ( "log/syslog" "github.com/sirupsen/logrus" - logrus_syslog "github.com/sirupsen/logrus/hooks/syslog" + lSyslog "github.com/sirupsen/logrus/hooks/syslog" ) func main() { log := logrus.New() - hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") + hook, err := lSyslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") if err == nil { log.Hooks.Add(hook) @@ -25,12 +25,12 @@ If you want to connect to local syslog (Ex. "/dev/log" or "/var/run/syslog" or " import ( "log/syslog" "github.com/sirupsen/logrus" - logrus_syslog "github.com/sirupsen/logrus/hooks/syslog" + lSyslog "github.com/sirupsen/logrus/hooks/syslog" ) func main() { log := logrus.New() - hook, err := logrus_syslog.NewSyslogHook("", "", syslog.LOG_INFO, "") + hook, err := lSyslog.NewSyslogHook("", "", syslog.LOG_INFO, "") if err == nil { log.Hooks.Add(hook) diff --git a/hooks/syslog/syslog.go b/hooks/syslog/syslog.go index 204f001..329ce0d 100644 --- a/hooks/syslog/syslog.go +++ b/hooks/syslog/syslog.go @@ -1,12 +1,13 @@ // +build !windows,!nacl,!plan9 -package logrus_syslog +package syslog import ( "fmt" - "github.com/sirupsen/logrus" "log/syslog" "os" + + "github.com/sirupsen/logrus" ) // SyslogHook to send logs via syslog. diff --git a/hooks/syslog/syslog_test.go b/hooks/syslog/syslog_test.go index 8d7fbe4..5ec3a44 100644 --- a/hooks/syslog/syslog_test.go +++ b/hooks/syslog/syslog_test.go @@ -1,9 +1,10 @@ -package logrus_syslog +package syslog import ( - "github.com/sirupsen/logrus" "log/syslog" "testing" + + "github.com/sirupsen/logrus" ) func TestLocalhostAddAndPrint(t *testing.T) { From fdea1df936cda9bf0e89ce053634e44890af4ab6 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Wed, 19 Jul 2017 16:24:10 +0200 Subject: [PATCH 085/111] add joonix/log to the formatters Closes #520 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 82aeb4e..7622d01 100644 --- a/README.md +++ b/README.md @@ -372,6 +372,7 @@ The built-in logging formatters are: Third party logging formatters: +* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can by parsed by Kubernetes and Google Container Engine. * [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events. * [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout. * [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. From bfff600029f56a858cd59cc32553fdb83731bc4f Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Wed, 19 Jul 2017 16:31:49 +0200 Subject: [PATCH 086/111] test all subpackages too --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ceace13..c6e1e44 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,4 +9,4 @@ env: install: - go get github.com/stretchr/testify/assert script: - - go test -race -v . + - go test -race -v ./... From a4149b6fd15cd323e3ebac0e985206539fd1b842 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Wed, 19 Jul 2017 16:53:07 +0200 Subject: [PATCH 087/111] travis needs the airbrake hook --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c6e1e44..69eb5a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,5 +8,6 @@ env: - GOMAXPROCS=4 GORACE=halt_on_error=1 install: - go get github.com/stretchr/testify/assert + - go get gopkg.in/gemnasium/logrus-airbrake-hook.v2 script: - go test -race -v ./... From 00386b3fbd637582f90cb17482dc3087646c0ac2 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Wed, 19 Jul 2017 17:47:53 +0200 Subject: [PATCH 088/111] remove unfinished doc sentence Closes #521 --- logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logger.go b/logger.go index b44966f..2acab05 100644 --- a/logger.go +++ b/logger.go @@ -25,7 +25,7 @@ type Logger struct { Formatter Formatter // The logging level the logger should log at. This is typically (and defaults // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be - // logged. `logrus.Debug` is useful in + // logged. Level Level // Used to sync writing to the log. Locking is enabled by Default mu MutexWrap From b264ba77c38df37dfa1eaa9fb31b2b898d4461e3 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Fri, 21 Jul 2017 16:06:23 +0200 Subject: [PATCH 089/111] add test in text formatter checking the formatting --- text_formatter_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/text_formatter_test.go b/text_formatter_test.go index ecb8f12..7f7b78c 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -9,6 +9,25 @@ import ( "time" ) +func TestFormatting(t *testing.T) { + tf := &TextFormatter{DisableColors: true} + + testCases := []struct { + value string + expected string + }{ + {`foo`, "time=\"0001-01-01T00:00:00Z\" level=panic test=foo \n"}, + } + + for _, tc := range testCases { + b, _ := tf.Format(WithField("test", tc.value)) + + if string(b) != tc.expected { + t.Errorf("formatting expected for %q (result was %q instead of %q)", tc.value, string(b), tc.expected) + } + } +} + func TestQuoting(t *testing.T) { tf := &TextFormatter{DisableColors: true} From 159e991025068d28cccc4138c1c19ff93b1e1df3 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Fri, 21 Jul 2017 16:14:28 +0200 Subject: [PATCH 090/111] only add a space between text entries if there is already content So we don't have a trailing space at the end of each log line --- text_formatter.go | 5 +++-- text_formatter_test.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index cf3f17f..ec8d473 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -154,11 +154,12 @@ func (f *TextFormatter) needsQuoting(text string) bool { } func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { - + if b.String() != "" { + b.WriteByte(' ') + } b.WriteString(key) b.WriteByte('=') f.appendValue(b, value) - b.WriteByte(' ') } func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { diff --git a/text_formatter_test.go b/text_formatter_test.go index 7f7b78c..d93b931 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -16,7 +16,7 @@ func TestFormatting(t *testing.T) { value string expected string }{ - {`foo`, "time=\"0001-01-01T00:00:00Z\" level=panic test=foo \n"}, + {`foo`, "time=\"0001-01-01T00:00:00Z\" level=panic test=foo\n"}, } for _, tc := range testCases { From 211aba39c8ddc81912160caa0edea2d61555efef Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Tue, 25 Jul 2017 09:39:35 +0200 Subject: [PATCH 091/111] use buffer length to avoid generating strings every time --- text_formatter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text_formatter.go b/text_formatter.go index ec8d473..79239cf 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -154,7 +154,7 @@ func (f *TextFormatter) needsQuoting(text string) bool { } func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { - if b.String() != "" { + if b.Len() > 0 { b.WriteByte(' ') } b.WriteString(key) From a663abbf1309d8542e0ea0f768536656e7993ef2 Mon Sep 17 00:00:00 2001 From: Huang Huang Date: Tue, 25 Jul 2017 22:52:40 +0800 Subject: [PATCH 092/111] readme: remove duplicate godoc link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7622d01..4f5ce57 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Logrus :walrus: [![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/sirupsen/logrus?status.svg)](https://godoc.org/github.com/sirupsen/logrus) Logrus is a structured logger for Go (golang), completely API compatible with -the standard library logger. [Godoc][godoc]. +the standard library logger. **Seeing weird case-sensitive problems?** It's in the past been possible to import Logrus as both upper- and lower-case. Due to the Go package environment, From 31e110ccae20b0bff6c04c43d5a78f2a90a248ff Mon Sep 17 00:00:00 2001 From: Davide D'Agostino Date: Tue, 25 Jul 2017 14:20:14 -0700 Subject: [PATCH 093/111] IsTerminal should work in nacl too I'm running logrus on a nacl environment and I get: ``` github.com/sirupsen/logrus/text_formatter.go:70: undefined: IsTerminal ``` --- terminal_notwindows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terminal_notwindows.go b/terminal_notwindows.go index 190297a..c3b7403 100644 --- a/terminal_notwindows.go +++ b/terminal_notwindows.go @@ -3,7 +3,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build linux darwin freebsd openbsd netbsd dragonfly +// +build linux darwin freebsd openbsd netbsd dragonfly nacl // +build !appengine package logrus From a9ca4bfe681a1e5f50e7af86117e634eaf05d7f4 Mon Sep 17 00:00:00 2001 From: Christy Perez Date: Tue, 30 May 2017 15:37:58 -0400 Subject: [PATCH 094/111] switch terminal_linux to x/sys/unix from syscall Closes sirupsen/logrus/issues/515 Signed-off-by: Christy Perez --- terminal_linux.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terminal_linux.go b/terminal_linux.go index 308160c..c13a944 100644 --- a/terminal_linux.go +++ b/terminal_linux.go @@ -7,8 +7,8 @@ package logrus -import "syscall" +import "golang.org/x/sys/unix" -const ioctlReadTermios = syscall.TCGETS +const ioctlReadTermios = unix.TCGETS type Termios syscall.Termios From 0af92424f930d002655a7168c88668a6af2c332f Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Tue, 25 Jul 2017 10:26:58 +0200 Subject: [PATCH 095/111] install x/sys/unix before running the travis tests --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 69eb5a4..28a702a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,5 +9,6 @@ env: install: - go get github.com/stretchr/testify/assert - go get gopkg.in/gemnasium/logrus-airbrake-hook.v2 + - go get golang.org/x/sys/unix script: - go test -race -v ./... From f4125cea1b9b2e7222fbdc04834cbc58fd839028 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Tue, 25 Jul 2017 10:29:46 +0200 Subject: [PATCH 096/111] get Termios from x/sys/unix --- terminal_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terminal_linux.go b/terminal_linux.go index c13a944..88d7298 100644 --- a/terminal_linux.go +++ b/terminal_linux.go @@ -11,4 +11,4 @@ import "golang.org/x/sys/unix" const ioctlReadTermios = unix.TCGETS -type Termios syscall.Termios +type Termios unix.Termios From 8a90bf3fffd416752e41f02f87759339e15cc504 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Wed, 26 Jul 2017 10:15:24 +0200 Subject: [PATCH 097/111] use x/sys for non-unix files --- .travis.yml | 1 + terminal_bsd.go | 6 +++--- terminal_notwindows.go | 5 +++-- terminal_windows.go | 11 ++++++----- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 28a702a..a23296a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,5 +10,6 @@ install: - go get github.com/stretchr/testify/assert - go get gopkg.in/gemnasium/logrus-airbrake-hook.v2 - go get golang.org/x/sys/unix + - go get golang.org/x/sys/windows script: - go test -race -v ./... diff --git a/terminal_bsd.go b/terminal_bsd.go index 5f6be4d..d7b3893 100644 --- a/terminal_bsd.go +++ b/terminal_bsd.go @@ -3,8 +3,8 @@ package logrus -import "syscall" +import "golang.org/x/sys/unix" -const ioctlReadTermios = syscall.TIOCGETA +const ioctlReadTermios = unix.TIOCGETA -type Termios syscall.Termios +type Termios unix.Termios diff --git a/terminal_notwindows.go b/terminal_notwindows.go index 190297a..198175b 100644 --- a/terminal_notwindows.go +++ b/terminal_notwindows.go @@ -11,8 +11,9 @@ package logrus import ( "io" "os" - "syscall" "unsafe" + + "golang.org/x/sys/unix" ) // IsTerminal returns true if stderr's file descriptor is a terminal. @@ -20,7 +21,7 @@ func IsTerminal(f io.Writer) bool { var termios Termios switch v := f.(type) { case *os.File: - _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(v.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + _, _, err := unix.Syscall6(unix.SYS_IOCTL, uintptr(v.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) return err == 0 default: return false diff --git a/terminal_windows.go b/terminal_windows.go index 7a33630..db9a8fb 100644 --- a/terminal_windows.go +++ b/terminal_windows.go @@ -15,11 +15,12 @@ import ( "os/exec" "strconv" "strings" - "syscall" "unsafe" + + "golang.org/x/sys/windows" ) -var kernel32 = syscall.NewLazyDLL("kernel32.dll") +var kernel32 = windows.NewLazyDLL("kernel32.dll") var ( procGetConsoleMode = kernel32.NewProc("GetConsoleMode") @@ -41,7 +42,7 @@ func getVersion() (float64, error) { if err != nil { return -1, err } - + // The output should be like "Microsoft Windows [Version XX.X.XXXXXX]" version := strings.Replace(stdout.String(), "\n", "", -1) version = strings.Replace(version, "\r\n", "", -1) @@ -64,7 +65,7 @@ func init() { // Activate Virtual Processing for Windows CMD // Info: https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx if ver >= 10 { - handle := syscall.Handle(os.Stderr.Fd()) + handle := windows.Handle(os.Stderr.Fd()) procSetConsoleMode.Call(uintptr(handle), enableProcessedOutput|enableWrapAtEolOutput|enableVirtualTerminalProcessing) } } @@ -74,7 +75,7 @@ func IsTerminal(f io.Writer) bool { switch v := f.(type) { case *os.File: var st uint32 - r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0) + r, _, e := windows.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0) return r != 0 && e == 0 default: return false From 325575f18110626a8fd7f027f899aa8c8f5169c6 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Wed, 26 Jul 2017 14:26:30 +0200 Subject: [PATCH 098/111] fix golint issues in formatters --- formatter.go | 2 +- json_formatter.go | 9 +++++++-- text_formatter.go | 4 +++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/formatter.go b/formatter.go index b5fbe93..b183ff5 100644 --- a/formatter.go +++ b/formatter.go @@ -2,7 +2,7 @@ package logrus import "time" -const DefaultTimestampFormat = time.RFC3339 +const defaultTimestampFormat = time.RFC3339 // The Formatter interface is used to implement a custom Formatter. It takes an // `Entry`. It exposes all the fields, including the default ones: diff --git a/json_formatter.go b/json_formatter.go index e787ea1..fb01c1b 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -6,8 +6,11 @@ import ( ) type fieldKey string + +// FieldMap allows customization of the key names for default fields. type FieldMap map[fieldKey]string +// Default key names for the default fields const ( FieldKeyMsg = "msg" FieldKeyLevel = "level" @@ -22,6 +25,7 @@ func (f FieldMap) resolve(key fieldKey) string { return string(key) } +// JSONFormatter formats logs into parsable json type JSONFormatter struct { // TimestampFormat sets the format used for marshaling timestamps. TimestampFormat string @@ -29,7 +33,7 @@ type JSONFormatter struct { // DisableTimestamp allows disabling automatic timestamps in output DisableTimestamp bool - // FieldMap allows users to customize the names of keys for various fields. + // FieldMap allows users to customize the names of keys for default fields. // As an example: // formatter := &JSONFormatter{ // FieldMap: FieldMap{ @@ -41,6 +45,7 @@ type JSONFormatter struct { FieldMap FieldMap } +// Format renders a single log entry func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { data := make(Fields, len(entry.Data)+3) for k, v := range entry.Data { @@ -57,7 +62,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { timestampFormat := f.TimestampFormat if timestampFormat == "" { - timestampFormat = DefaultTimestampFormat + timestampFormat = defaultTimestampFormat } if !f.DisableTimestamp { diff --git a/text_formatter.go b/text_formatter.go index 79239cf..362fa28 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -26,6 +26,7 @@ func init() { baseTimestamp = time.Now() } +// TextFormatter formats logs into text type TextFormatter struct { // Set to true to bypass checking for a TTY before outputting colors. ForceColors bool @@ -64,6 +65,7 @@ func (f *TextFormatter) init(entry *Entry) { } } +// Format renders a single log entry func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { var b *bytes.Buffer keys := make([]string, 0, len(entry.Data)) @@ -88,7 +90,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { timestampFormat := f.TimestampFormat if timestampFormat == "" { - timestampFormat = DefaultTimestampFormat + timestampFormat = defaultTimestampFormat } if isColored { f.printColored(b, entry, keys, timestampFormat) From 6f38f401f7a504e0afe72cc11c2c56390c5a37ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20H=C3=A4nsel?= Date: Thu, 27 Jul 2017 05:03:02 +0200 Subject: [PATCH 099/111] Add appveyor.yml --- appveyor.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..96c2ce1 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,14 @@ +version: "{build}" +platform: x64 +clone_folder: c:\gopath\src\github.com\sirupsen\logrus +environment: + GOPATH: c:\gopath +branches: + only: + - master +install: + - set PATH=%GOPATH%\bin;c:\go\bin;%PATH% + - go version +build_script: + - go get -t + - go test From d4ae98b1775e4d5f870f8856505b5ff33e83df14 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Thu, 27 Jul 2017 14:06:49 +0200 Subject: [PATCH 100/111] improve test failure messages for alt_exit_test --- alt_exit_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/alt_exit_test.go b/alt_exit_test.go index d182963..71ed820 100644 --- a/alt_exit_test.go +++ b/alt_exit_test.go @@ -11,14 +11,14 @@ func TestRegister(t *testing.T) { current := len(handlers) RegisterExitHandler(func() {}) if len(handlers) != current+1 { - t.Fatalf("can't add handler") + t.Fatalf("expected %d handlers, got %d", current+1, len(handlers)) } } func TestHandler(t *testing.T) { gofile := "/tmp/testprog.go" if err := ioutil.WriteFile(gofile, testprog, 0666); err != nil { - t.Fatalf("can't create go file") + t.Fatalf("can't create go file. %q", err) } outfile := "/tmp/testprog.out" @@ -30,11 +30,11 @@ func TestHandler(t *testing.T) { data, err := ioutil.ReadFile(outfile) if err != nil { - t.Fatalf("can't read output file %s", outfile) + t.Fatalf("can't read output file %s. %q", outfile, err) } if string(data) != arg { - t.Fatalf("bad data") + t.Fatalf("bad data. Expected %q, got %q", data, arg) } } From 60f34385800db4d306b66ffbd8fd820c89bc6dc3 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Thu, 27 Jul 2017 14:46:09 +0200 Subject: [PATCH 101/111] fix creating temp files on non-unix platforms --- alt_exit_test.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/alt_exit_test.go b/alt_exit_test.go index 71ed820..a08b1a8 100644 --- a/alt_exit_test.go +++ b/alt_exit_test.go @@ -2,7 +2,10 @@ package logrus import ( "io/ioutil" + "log" + "os" "os/exec" + "path/filepath" "testing" "time" ) @@ -16,14 +19,20 @@ func TestRegister(t *testing.T) { } func TestHandler(t *testing.T) { - gofile := "/tmp/testprog.go" + tempDir, err := ioutil.TempDir("", "test_handler") + if err != nil { + log.Fatalf("can't create temp dir. %q", err) + } + defer os.RemoveAll(tempDir) + + gofile := filepath.Join(tempDir, "gofile.go") if err := ioutil.WriteFile(gofile, testprog, 0666); err != nil { t.Fatalf("can't create go file. %q", err) } - outfile := "/tmp/testprog.out" + outfile := filepath.Join(tempDir, "outfile.out") arg := time.Now().UTC().String() - err := exec.Command("go", "run", gofile, outfile, arg).Run() + err = exec.Command("go", "run", gofile, outfile, arg).Run() if err == nil { t.Fatalf("completed normally, should have failed") } From e66f22976feaed4e0a6ab43bc2b9226be8b0b979 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Thu, 27 Jul 2017 14:23:27 +0200 Subject: [PATCH 102/111] remove os-specific IsTerminal methods, and use x/crypto/ssh/terminal instead --- terminal_appengine.go | 10 ----- terminal_notwindows.go | 29 --------------- terminal_solaris.go | 21 ----------- terminal_windows.go | 83 ------------------------------------------ text_formatter.go | 15 +++++++- 5 files changed, 14 insertions(+), 144 deletions(-) delete mode 100644 terminal_appengine.go delete mode 100644 terminal_notwindows.go delete mode 100644 terminal_solaris.go delete mode 100644 terminal_windows.go diff --git a/terminal_appengine.go b/terminal_appengine.go deleted file mode 100644 index e011a86..0000000 --- a/terminal_appengine.go +++ /dev/null @@ -1,10 +0,0 @@ -// +build appengine - -package logrus - -import "io" - -// IsTerminal returns true if stderr's file descriptor is a terminal. -func IsTerminal(f io.Writer) bool { - return true -} diff --git a/terminal_notwindows.go b/terminal_notwindows.go deleted file mode 100644 index 9a4611e..0000000 --- a/terminal_notwindows.go +++ /dev/null @@ -1,29 +0,0 @@ -// Based on ssh/terminal: -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build linux darwin freebsd openbsd netbsd dragonfly nacl -// +build !appengine - -package logrus - -import ( - "io" - "os" - "unsafe" - - "golang.org/x/sys/unix" -) - -// IsTerminal returns true if stderr's file descriptor is a terminal. -func IsTerminal(f io.Writer) bool { - var termios Termios - switch v := f.(type) { - case *os.File: - _, _, err := unix.Syscall6(unix.SYS_IOCTL, uintptr(v.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) - return err == 0 - default: - return false - } -} diff --git a/terminal_solaris.go b/terminal_solaris.go deleted file mode 100644 index 3c86b1a..0000000 --- a/terminal_solaris.go +++ /dev/null @@ -1,21 +0,0 @@ -// +build solaris,!appengine - -package logrus - -import ( - "io" - "os" - - "golang.org/x/sys/unix" -) - -// IsTerminal returns true if the given file descriptor is a terminal. -func IsTerminal(f io.Writer) bool { - switch v := f.(type) { - case *os.File: - _, err := unix.IoctlGetTermios(int(v.Fd()), unix.TCGETA) - return err == nil - default: - return false - } -} diff --git a/terminal_windows.go b/terminal_windows.go deleted file mode 100644 index db9a8fb..0000000 --- a/terminal_windows.go +++ /dev/null @@ -1,83 +0,0 @@ -// Based on ssh/terminal: -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build windows,!appengine - -package logrus - -import ( - "bytes" - "errors" - "io" - "os" - "os/exec" - "strconv" - "strings" - "unsafe" - - "golang.org/x/sys/windows" -) - -var kernel32 = windows.NewLazyDLL("kernel32.dll") - -var ( - procGetConsoleMode = kernel32.NewProc("GetConsoleMode") - procSetConsoleMode = kernel32.NewProc("SetConsoleMode") -) - -const ( - enableProcessedOutput = 0x0001 - enableWrapAtEolOutput = 0x0002 - enableVirtualTerminalProcessing = 0x0004 -) - -func getVersion() (float64, error) { - stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{} - cmd := exec.Command("cmd", "ver") - cmd.Stdout = stdout - cmd.Stderr = stderr - err := cmd.Run() - if err != nil { - return -1, err - } - - // The output should be like "Microsoft Windows [Version XX.X.XXXXXX]" - version := strings.Replace(stdout.String(), "\n", "", -1) - version = strings.Replace(version, "\r\n", "", -1) - - x1 := strings.Index(version, "[Version") - - if x1 == -1 || strings.Index(version, "]") == -1 { - return -1, errors.New("Can't determine Windows version") - } - - return strconv.ParseFloat(version[x1+9:x1+13], 64) -} - -func init() { - ver, err := getVersion() - if err != nil { - return - } - - // Activate Virtual Processing for Windows CMD - // Info: https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx - if ver >= 10 { - handle := windows.Handle(os.Stderr.Fd()) - procSetConsoleMode.Call(uintptr(handle), enableProcessedOutput|enableWrapAtEolOutput|enableVirtualTerminalProcessing) - } -} - -// IsTerminal returns true if stderr's file descriptor is a terminal. -func IsTerminal(f io.Writer) bool { - switch v := f.(type) { - case *os.File: - var st uint32 - r, _, e := windows.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0) - return r != 0 && e == 0 - default: - return false - } -} diff --git a/text_formatter.go b/text_formatter.go index 362fa28..be412aa 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -3,10 +3,14 @@ package logrus import ( "bytes" "fmt" + "io" + "os" "sort" "strings" "sync" "time" + + "golang.org/x/crypto/ssh/terminal" ) const ( @@ -61,7 +65,16 @@ type TextFormatter struct { func (f *TextFormatter) init(entry *Entry) { if entry.Logger != nil { - f.isTerminal = IsTerminal(entry.Logger.Out) + f.isTerminal = f.checkIfTerminal(entry.Logger.Out) + } +} + +func (f *TextFormatter) checkIfTerminal(w io.Writer) bool { + switch v := w.(type) { + case *os.File: + return terminal.IsTerminal(int(v.Fd())) + default: + return false } } From 3bd397e07f1a30f5714572ef1dd5f3cd9266db87 Mon Sep 17 00:00:00 2001 From: Ross McDonald Date: Mon, 7 Aug 2017 16:36:23 -0500 Subject: [PATCH 103/111] Add Telegram hook to README.md. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4f5ce57..c9ff70e 100644 --- a/README.md +++ b/README.md @@ -285,6 +285,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [Sumorus](https://github.com/doublefree/sumorus) | Hook for logging to [SumoLogic](https://www.sumologic.com/)| | [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. | | [Syslog TLS](https://github.com/shinji62/logrus-syslog-ng) | Send errors to remote syslog server with TLS support. | +| [Telegram](https://github.com/rossmcdonald/telegram_hook) | Hook for logging errors to [Telegram](https://telegram.org/) | | [TraceView](https://github.com/evalphobia/logrus_appneta) | Hook for logging to [AppNeta TraceView](https://www.appneta.com/products/traceview/) | | [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) | | [logz.io](https://github.com/ripcurld00d/logrus-logzio-hook) | Hook for logging to [logz.io](https://logz.io), a Log as a Service using Logstash | From e3e7388b95dcee9be73daf15e41dc5b3acb911b3 Mon Sep 17 00:00:00 2001 From: Rafe Colton Date: Thu, 10 Aug 2017 10:46:39 -0400 Subject: [PATCH 104/111] Replace example files with testable examples --- example_basic_test.go | 69 +++++++++++++++++++++++++++++++++++++++++ example_hook_test.go | 35 +++++++++++++++++++++ examples/basic/basic.go | 59 ----------------------------------- examples/hook/hook.go | 30 ------------------ 4 files changed, 104 insertions(+), 89 deletions(-) create mode 100644 example_basic_test.go create mode 100644 example_hook_test.go delete mode 100644 examples/basic/basic.go delete mode 100644 examples/hook/hook.go diff --git a/example_basic_test.go b/example_basic_test.go new file mode 100644 index 0000000..a2acf55 --- /dev/null +++ b/example_basic_test.go @@ -0,0 +1,69 @@ +package logrus_test + +import ( + "github.com/sirupsen/logrus" + "os" +) + +func Example_basic() { + var log = logrus.New() + log.Formatter = new(logrus.JSONFormatter) + log.Formatter = new(logrus.TextFormatter) //default + log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true // remove timestamp from test output + log.Level = logrus.DebugLevel + log.Out = os.Stdout + + // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666) + // if err == nil { + // log.Out = file + // } else { + // log.Info("Failed to log to file, using default stderr") + // } + + defer func() { + err := recover() + if err != nil { + entry := err.(*logrus.Entry) + log.WithFields(logrus.Fields{ + "omg": true, + "err_animal": entry.Data["animal"], + "err_size": entry.Data["size"], + "err_level": entry.Level, + "err_message": entry.Message, + "number": 100, + }).Error("The ice breaks!") // or use Fatal() to force the process to exit with a nonzero code + } + }() + + log.WithFields(logrus.Fields{ + "animal": "walrus", + "number": 8, + }).Debug("Started observing beach") + + log.WithFields(logrus.Fields{ + "animal": "walrus", + "size": 10, + }).Info("A group of walrus emerges from the ocean") + + log.WithFields(logrus.Fields{ + "omg": true, + "number": 122, + }).Warn("The group's number increased tremendously!") + + log.WithFields(logrus.Fields{ + "temperature": -4, + }).Debug("Temperature changes") + + log.WithFields(logrus.Fields{ + "animal": "orca", + "size": 9009, + }).Panic("It's over 9000!") + + // Output: + // level=debug msg="Started observing beach" animal=walrus number=8 + // level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10 + // level=warning msg="The group's number increased tremendously!" number=122 omg=true + // level=debug msg="Temperature changes" temperature=-4 + // level=panic msg="It's over 9000!" animal=orca size=9009 + // level=error msg="The ice breaks!" err_animal=orca err_level=panic err_message="It's over 9000!" err_size=9009 number=100 omg=true +} diff --git a/example_hook_test.go b/example_hook_test.go new file mode 100644 index 0000000..d4ddffc --- /dev/null +++ b/example_hook_test.go @@ -0,0 +1,35 @@ +package logrus_test + +import ( + "github.com/sirupsen/logrus" + "gopkg.in/gemnasium/logrus-airbrake-hook.v2" + "os" +) + +func Example_hook() { + var log = logrus.New() + log.Formatter = new(logrus.TextFormatter) // default + log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true // remove timestamp from test output + log.Hooks.Add(airbrake.NewHook(123, "xyz", "development")) + log.Out = os.Stdout + + log.WithFields(logrus.Fields{ + "animal": "walrus", + "size": 10, + }).Info("A group of walrus emerges from the ocean") + + log.WithFields(logrus.Fields{ + "omg": true, + "number": 122, + }).Warn("The group's number increased tremendously!") + + log.WithFields(logrus.Fields{ + "omg": true, + "number": 100, + }).Error("The ice breaks!") + + // Output: + // level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10 + // level=warning msg="The group's number increased tremendously!" number=122 omg=true + // level=error msg="The ice breaks!" number=100 omg=true +} diff --git a/examples/basic/basic.go b/examples/basic/basic.go deleted file mode 100644 index 3e112b4..0000000 --- a/examples/basic/basic.go +++ /dev/null @@ -1,59 +0,0 @@ -package main - -import ( - "github.com/sirupsen/logrus" - // "os" -) - -var log = logrus.New() - -func init() { - log.Formatter = new(logrus.JSONFormatter) - log.Formatter = new(logrus.TextFormatter) // default - - // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666) - // if err == nil { - // log.Out = file - // } else { - // log.Info("Failed to log to file, using default stderr") - // } - - log.Level = logrus.DebugLevel -} - -func main() { - defer func() { - err := recover() - if err != nil { - log.WithFields(logrus.Fields{ - "omg": true, - "err": err, - "number": 100, - }).Fatal("The ice breaks!") - } - }() - - log.WithFields(logrus.Fields{ - "animal": "walrus", - "number": 8, - }).Debug("Started observing beach") - - log.WithFields(logrus.Fields{ - "animal": "walrus", - "size": 10, - }).Info("A group of walrus emerges from the ocean") - - log.WithFields(logrus.Fields{ - "omg": true, - "number": 122, - }).Warn("The group's number increased tremendously!") - - log.WithFields(logrus.Fields{ - "temperature": -4, - }).Debug("Temperature changes") - - log.WithFields(logrus.Fields{ - "animal": "orca", - "size": 9009, - }).Panic("It's over 9000!") -} diff --git a/examples/hook/hook.go b/examples/hook/hook.go deleted file mode 100644 index c8470c3..0000000 --- a/examples/hook/hook.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "github.com/sirupsen/logrus" - "gopkg.in/gemnasium/logrus-airbrake-hook.v2" -) - -var log = logrus.New() - -func init() { - log.Formatter = new(logrus.TextFormatter) // default - log.Hooks.Add(airbrake.NewHook(123, "xyz", "development")) -} - -func main() { - log.WithFields(logrus.Fields{ - "animal": "walrus", - "size": 10, - }).Info("A group of walrus emerges from the ocean") - - log.WithFields(logrus.Fields{ - "omg": true, - "number": 122, - }).Warn("The group's number increased tremendously!") - - log.WithFields(logrus.Fields{ - "omg": true, - "number": 100, - }).Fatal("The ice breaks!") -} From f006c2ac4710855cf0f916dd6b77acf6b048dc6e Mon Sep 17 00:00:00 2001 From: Paul Seiffert Date: Tue, 15 Aug 2017 22:20:55 +0200 Subject: [PATCH 105/111] changelog: bump to v1.0.3 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c443aed..8236d8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.0.3 + +* Replace example files with testable examples + # 1.0.2 * bug: quote non-string values in text formatter (#583) From 5efed00cb047b9812e5947f9aad52512e7c75e13 Mon Sep 17 00:00:00 2001 From: Tracer Tong Date: Thu, 17 Aug 2017 16:33:45 +0900 Subject: [PATCH 106/111] Update README.md to fix link to Kafka hook Current link is broken. I have made a mix from couple of dead projects and I think I will support my version for long time. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4f5ce57..e698386 100644 --- a/README.md +++ b/README.md @@ -260,7 +260,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb | | [Influxus](http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB](http://influxdata.com/) | | [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` | -| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka | +| [KafkaLogrus](https://github.com/tracer0tong/kafkalogrus) | Hook for logging to Kafka | | [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem | | [Logentries](https://github.com/jcftang/logentriesrus) | Hook for logging to [Logentries](https://logentries.com/) | | [Logentrus](https://github.com/puddingfactory/logentrus) | Hook for logging to [Logentries](https://logentries.com/) | From 3d1341ce2c22580f905a6422decdffc688ffbb08 Mon Sep 17 00:00:00 2001 From: Aditya Mukerjee Date: Fri, 4 Aug 2017 11:24:22 -0400 Subject: [PATCH 107/111] Add AddHook method for logger --- logger.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/logger.go b/logger.go index 2acab05..fdaf8a6 100644 --- a/logger.go +++ b/logger.go @@ -315,3 +315,9 @@ func (logger *Logger) level() Level { func (logger *Logger) SetLevel(level Level) { atomic.StoreUint32((*uint32)(&logger.Level), uint32(level)) } + +func (logger *Logger) AddHook(hook Hook) { + logger.mu.Lock() + defer logger.mu.Unlock() + logger.Hooks.Add(hook) +} From 66230b287179d93b8a9ca65d5e08495b24163535 Mon Sep 17 00:00:00 2001 From: Aditya Mukerjee Date: Fri, 4 Aug 2017 13:00:10 -0400 Subject: [PATCH 108/111] Add test for race condition in hooks --- hook_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hook_test.go b/hook_test.go index 13f34cb..d14d1e3 100644 --- a/hook_test.go +++ b/hook_test.go @@ -1,6 +1,7 @@ package logrus import ( + "sync" "testing" "github.com/stretchr/testify/assert" @@ -120,3 +121,22 @@ func TestErrorHookShouldFireOnError(t *testing.T) { assert.Equal(t, hook.Fired, true) }) } + +func TestAddHookRace(t *testing.T) { + var wg sync.WaitGroup + wg.Add(2) + hook := new(ErrorHook) + LogAndAssertJSON(t, func(log *Logger) { + go func() { + defer wg.Done() + log.AddHook(hook) + }() + go func() { + defer wg.Done() + log.Error("test") + }() + wg.Wait() + }, func(fields Fields) { + assert.Equal(t, hook.Fired, true) + }) +} From c830992a61feb7c379da5708645b44cf27041a02 Mon Sep 17 00:00:00 2001 From: Aditya Mukerjee Date: Fri, 4 Aug 2017 13:00:43 -0400 Subject: [PATCH 109/111] Take lock on mutex when firing hooks --- entry.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/entry.go b/entry.go index 5bf582e..1fad45e 100644 --- a/entry.go +++ b/entry.go @@ -94,7 +94,10 @@ func (entry Entry) log(level Level, msg string) { entry.Level = level entry.Message = msg - if err := entry.Logger.Hooks.Fire(level, &entry); err != nil { + entry.Logger.mu.Lock() + err := entry.Logger.Hooks.Fire(level, &entry) + entry.Logger.mu.Unlock() + if err != nil { entry.Logger.mu.Lock() fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) entry.Logger.mu.Unlock() From 9bc52e39813590fd3b61e23badfc85c3bbee2a73 Mon Sep 17 00:00:00 2001 From: Aditya Mukerjee Date: Fri, 4 Aug 2017 13:01:04 -0400 Subject: [PATCH 110/111] Fix test assertion --- hook_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hook_test.go b/hook_test.go index d14d1e3..4fea751 100644 --- a/hook_test.go +++ b/hook_test.go @@ -137,6 +137,8 @@ func TestAddHookRace(t *testing.T) { }() wg.Wait() }, func(fields Fields) { - assert.Equal(t, hook.Fired, true) + // the line may have been logged + // before the hook was added, so we can't + // actually assert on the hook }) } From cd1114dc254e89e1ba0c9abb7145eadd90c2244b Mon Sep 17 00:00:00 2001 From: Ken Faulkner Date: Sun, 20 Aug 2017 12:47:38 +1000 Subject: [PATCH 111/111] Added reference to AzureTableHook --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 83b3b80..5f656c3 100644 --- a/README.md +++ b/README.md @@ -247,6 +247,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. | | [Amazon Kinesis](https://github.com/evalphobia/logrus_kinesis) | Hook for logging to [Amazon Kinesis](https://aws.amazon.com/kinesis/) | | [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) | +| [AzureTableHook](https://github.com/kpfaulkner/azuretablehook/) | Hook for logging to Azure Table Storage| | [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. | | [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic | | [Discordrus](https://github.com/kz/discordrus) | Hook for logging to [Discord](https://discordapp.com/) |