From 70ec483753d9488b8be57f6322ca65f5c3361164 Mon Sep 17 00:00:00 2001 From: Burke Libbey Date: Mon, 16 Mar 2015 15:29:39 -0400 Subject: [PATCH 001/547] hooks: Add BugSnag hook --- hooks/bugsnag/bugsnag.go | 53 +++++++++++++++++++++++++++++ hooks/bugsnag/bugsnag_test.go | 64 +++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 hooks/bugsnag/bugsnag.go create mode 100644 hooks/bugsnag/bugsnag_test.go diff --git a/hooks/bugsnag/bugsnag.go b/hooks/bugsnag/bugsnag.go new file mode 100644 index 000000000..a0cfe8446 --- /dev/null +++ b/hooks/bugsnag/bugsnag.go @@ -0,0 +1,53 @@ +package logrus_bugsnag + +import ( + "github.com/Sirupsen/logrus" + "github.com/bugsnag/bugsnag-go" +) + +// BugsnagHook sends exceptions to an exception-tracking service compatible +// with the Bugsnag API. Before using this hook, you must call +// bugsnag.Configure(). +// +// Entries that trigger an Error, Fatal or Panic should now include an "error" +// field to send to Bugsnag +type BugsnagHook struct{} + +// Fire forwards an error to Bugsnag. Given a logrus.Entry, it extracts the +// implicitly-required "error" field and sends it off. +func (hook *BugsnagHook) Fire(entry *logrus.Entry) error { + if entry.Data["error"] == nil { + entry.Logger.WithFields(logrus.Fields{ + "source": "bugsnag", + }).Warn("Exceptions sent to Bugsnag must have an 'error' key with the error") + return nil + } + + err, ok := entry.Data["error"].(error) + if !ok { + entry.Logger.WithFields(logrus.Fields{ + "source": "bugsnag", + }).Warn("Exceptions sent to Bugsnag must have an `error` key of type `error`") + return nil + } + + bugsnagErr := bugsnag.Notify(err) + if bugsnagErr != nil { + entry.Logger.WithFields(logrus.Fields{ + "source": "bugsnag", + "error": bugsnagErr, + }).Warn("Failed to send error to Bugsnag") + } + + return nil +} + +// Levels enumerates the log levels on which the error should be forwarded to +// bugsnag: everything at or above the "Error" level. +func (hook *BugsnagHook) Levels() []logrus.Level { + return []logrus.Level{ + logrus.ErrorLevel, + logrus.FatalLevel, + logrus.PanicLevel, + } +} diff --git a/hooks/bugsnag/bugsnag_test.go b/hooks/bugsnag/bugsnag_test.go new file mode 100644 index 000000000..7db5136e9 --- /dev/null +++ b/hooks/bugsnag/bugsnag_test.go @@ -0,0 +1,64 @@ +package logrus_bugsnag + +import ( + "encoding/json" + "errors" + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/Sirupsen/logrus" + "github.com/bugsnag/bugsnag-go" +) + +type notice struct { + Events []struct { + Exceptions []struct { + Message string `json:"message"` + } `json:"exceptions"` + } `json:"events"` +} + +func TestNoticeReceived(t *testing.T) { + msg := make(chan string, 1) + expectedMsg := "foo" + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var notice notice + data, _ := ioutil.ReadAll(r.Body) + if err := json.Unmarshal(data, ¬ice); err != nil { + t.Error(err) + } + _ = r.Body.Close() + + msg <- notice.Events[0].Exceptions[0].Message + })) + defer ts.Close() + + hook := &BugsnagHook{} + + bugsnag.Configure(bugsnag.Configuration{ + Endpoint: ts.URL, + ReleaseStage: "production", + APIKey: "12345678901234567890123456789012", + Synchronous: true, + }) + + log := logrus.New() + log.Hooks.Add(hook) + + log.WithFields(logrus.Fields{ + "error": errors.New(expectedMsg), + }).Error("Bugsnag will not see this string") + + select { + case received := <-msg: + if received != expectedMsg { + t.Errorf("Unexpected message received: %s", received) + } + case <-time.After(time.Second): + t.Error("Timed out; no notice received by Bugsnag API") + } +} From 0628e2348d8d34afcf9215ccebc321517f79e129 Mon Sep 17 00:00:00 2001 From: Burke Libbey Date: Thu, 19 Mar 2015 11:17:22 -0400 Subject: [PATCH 002/547] Code review changes --- README.md | 6 ++++ hooks/bugsnag/bugsnag.go | 65 +++++++++++++++++++++-------------- hooks/bugsnag/bugsnag_test.go | 2 +- 3 files changed, 47 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index e755e7c18..e3b5afab5 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ import ( "os" log "github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus/hooks/airbrake" + "github.com/Sirupsen/logrus/hooks/bugsnag" ) func init() { @@ -84,6 +85,11 @@ func init() { // an exception tracker. You can create custom hooks, see the Hooks section. log.AddHook(&logrus_airbrake.AirbrakeHook{}) + // Use the Bugsnag hook to report errors that have Error severity or above to + // an exception tracker. You can create custom hooks, see the Hooks section. + bugsnagHook, _ = logrus_bugsnag.NewBugsnagHook() + log.AddHook(bugsnagHook) + // Output to stderr instead of stdout, could also be a file. log.SetOutput(os.Stderr) diff --git a/hooks/bugsnag/bugsnag.go b/hooks/bugsnag/bugsnag.go index a0cfe8446..d20a0f54a 100644 --- a/hooks/bugsnag/bugsnag.go +++ b/hooks/bugsnag/bugsnag.go @@ -1,42 +1,57 @@ package logrus_bugsnag import ( + "errors" + "github.com/Sirupsen/logrus" "github.com/bugsnag/bugsnag-go" ) -// BugsnagHook sends exceptions to an exception-tracking service compatible -// with the Bugsnag API. Before using this hook, you must call -// bugsnag.Configure(). +type bugsnagHook struct{} + +// ErrBugsnagUnconfigured is returned if NewBugsnagHook is called before +// bugsnag.Configure. Bugsnag must be configured before the hook. +var ErrBugsnagUnconfigured = errors.New("bugsnag must be configured before installing this logrus hook") + +// ErrBugsnagSendFailed indicates that the hook failed to submit an error to +// bugsnag. The error was successfully generated, but `bugsnag.Notify()` +// failed. +type ErrBugsnagSendFailed struct { + err error +} + +func (e ErrBugsnagSendFailed) Error() string { + return "failed to send error to Bugsnag: " + e.err.Error() +} + +// NewBugsnagHook initializes a logrus hook which sends exceptions to an +// exception-tracking service compatible with the Bugsnag API. Before using +// this hook, you must call bugsnag.Configure(). The returned object should be +// registered with a log via `AddHook()` // // Entries that trigger an Error, Fatal or Panic should now include an "error" -// field to send to Bugsnag -type BugsnagHook struct{} - -// Fire forwards an error to Bugsnag. Given a logrus.Entry, it extracts the -// implicitly-required "error" field and sends it off. -func (hook *BugsnagHook) Fire(entry *logrus.Entry) error { - if entry.Data["error"] == nil { - entry.Logger.WithFields(logrus.Fields{ - "source": "bugsnag", - }).Warn("Exceptions sent to Bugsnag must have an 'error' key with the error") - return nil +// field to send to Bugsnag. +func NewBugsnagHook() (*bugsnagHook, error) { + if bugsnag.Config.APIKey == "" { + return nil, ErrBugsnagUnconfigured } + return &bugsnagHook{}, nil +} +// Fire forwards an error to Bugsnag. Given a logrus.Entry, it extracts the +// "error" field (or the Message if the error isn't present) and sends it off. +func (hook *bugsnagHook) Fire(entry *logrus.Entry) error { + var notifyErr error err, ok := entry.Data["error"].(error) - if !ok { - entry.Logger.WithFields(logrus.Fields{ - "source": "bugsnag", - }).Warn("Exceptions sent to Bugsnag must have an `error` key of type `error`") - return nil + if ok { + notifyErr = err + } else { + notifyErr = errors.New(entry.Message) } - bugsnagErr := bugsnag.Notify(err) + bugsnagErr := bugsnag.Notify(notifyErr) if bugsnagErr != nil { - entry.Logger.WithFields(logrus.Fields{ - "source": "bugsnag", - "error": bugsnagErr, - }).Warn("Failed to send error to Bugsnag") + return ErrBugsnagSendFailed{bugsnagErr} } return nil @@ -44,7 +59,7 @@ func (hook *BugsnagHook) Fire(entry *logrus.Entry) error { // Levels enumerates the log levels on which the error should be forwarded to // bugsnag: everything at or above the "Error" level. -func (hook *BugsnagHook) Levels() []logrus.Level { +func (hook *bugsnagHook) Levels() []logrus.Level { return []logrus.Level{ logrus.ErrorLevel, logrus.FatalLevel, diff --git a/hooks/bugsnag/bugsnag_test.go b/hooks/bugsnag/bugsnag_test.go index 7db5136e9..e9ea298d8 100644 --- a/hooks/bugsnag/bugsnag_test.go +++ b/hooks/bugsnag/bugsnag_test.go @@ -37,7 +37,7 @@ func TestNoticeReceived(t *testing.T) { })) defer ts.Close() - hook := &BugsnagHook{} + hook := &bugsnagHook{} bugsnag.Configure(bugsnag.Configuration{ Endpoint: ts.URL, From dbe5e50341259b8955b345d23f124fcf11b779f4 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 19 Mar 2015 11:26:39 -0400 Subject: [PATCH 003/547] readme: move bugsnag docs --- README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 61ba24820..512f26e5e 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,6 @@ import ( "os" log "github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus/hooks/airbrake" - "github.com/Sirupsen/logrus/hooks/bugsnag" ) func init() { @@ -85,11 +84,6 @@ func init() { // an exception tracker. You can create custom hooks, see the Hooks section. log.AddHook(airbrake.NewHook("https://example.com", "xyz", "development")) - // Use the Bugsnag hook to report errors that have Error severity or above to - // an exception tracker. You can create custom hooks, see the Hooks section. - bugsnagHook, _ = logrus_bugsnag.NewBugsnagHook() - log.AddHook(bugsnagHook) - // Output to stderr instead of stdout, could also be a file. log.SetOutput(os.Stderr) @@ -204,6 +198,9 @@ func init() { Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. +* [`github.com/Sirupsen/logrus/hooks/bugsnag`](https://github.com/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go) + Send errors to the Bugsnag exception tracking service. + * [`github.com/nubo/hiprus`](https://github.com/nubo/hiprus) Send errors to a channel in hipchat. From d72374a6c13ad50ec923dfd2ab57ce79bd6a3ad5 Mon Sep 17 00:00:00 2001 From: Tiffany Low Date: Thu, 26 Mar 2015 16:04:45 -0700 Subject: [PATCH 004/547] allow custom time format string in TextFormatter - fixes examples in README.md that incorrectly state usage of RFC3339Nano format instead of RFC3339 --- README.md | 12 +++++++----- text_formatter.go | 15 +++++++++++---- text_formatter_test.go | 26 +++++++++++++++++++++++++- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 512f26e5e..bf09541e8 100644 --- a/README.md +++ b/README.md @@ -37,11 +37,13 @@ attached, the output is compatible with the [logfmt](http://godoc.org/github.com/kr/logfmt) format: ```text -time="2014-04-20 15:36:23.830442383 -0400 EDT" level="info" msg="A group of walrus emerges from the ocean" animal="walrus" size=10 -time="2014-04-20 15:36:23.830584199 -0400 EDT" level="warning" msg="The group's number increased tremendously!" omg=true number=122 -time="2014-04-20 15:36:23.830596521 -0400 EDT" level="info" msg="A giant walrus appears!" animal="walrus" size=10 -time="2014-04-20 15:36:23.830611837 -0400 EDT" level="info" msg="Tremendously sized cow enters the ocean." animal="walrus" size=9 -time="2014-04-20 15:36:23.830626464 -0400 EDT" level="fatal" msg="The ice breaks!" omg=true number=100 +time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8 +time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10 +time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true +time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4 +time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009 +time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true +exit status 1 ``` #### Example diff --git a/text_formatter.go b/text_formatter.go index 0a06a1105..d3687ba25 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -18,8 +18,9 @@ const ( ) var ( - baseTimestamp time.Time - isTerminal bool + baseTimestamp time.Time + isTerminal bool + defaultTimestampFormat = time.RFC3339 ) func init() { @@ -46,6 +47,9 @@ type TextFormatter struct { // the time passed since beginning of execution. FullTimestamp bool + // Timestamp format to use for display, if a full timestamp is printed + TimestampFormat string + // The fields are sorted by default for a consistent output. For applications // that log extremely frequently and don't use the JSON formatter this may not // be desired. @@ -68,11 +72,14 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { isColored := (f.ForceColors || isTerminal) && !f.DisableColors + if f.TimestampFormat == "" { + f.TimestampFormat = defaultTimestampFormat + } if isColored { f.printColored(b, entry, keys) } else { if !f.DisableTimestamp { - f.appendKeyValue(b, "time", entry.Time.Format(time.RFC3339)) + f.appendKeyValue(b, "time", entry.Time.Format(f.TimestampFormat)) } f.appendKeyValue(b, "level", entry.Level.String()) f.appendKeyValue(b, "msg", entry.Message) @@ -103,7 +110,7 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin if !f.FullTimestamp { fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) } else { - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(time.RFC3339), entry.Message) + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(f.TimestampFormat), entry.Message) } for _, k := range keys { v := entry.Data[k] diff --git a/text_formatter_test.go b/text_formatter_test.go index 28a949907..e25a44f67 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -3,8 +3,8 @@ package logrus import ( "bytes" "errors" - "testing" + "time" ) func TestQuoting(t *testing.T) { @@ -33,5 +33,29 @@ func TestQuoting(t *testing.T) { checkQuoting(true, errors.New("invalid argument")) } +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 : timeEnd-1] + if timeStr[0] == '"' && timeStr[len(timeStr)-1] == '"' { + timeStr = timeStr[1 : len(timeStr)-1] + } + if format == "" { + format = time.RFC3339 + } + _, e := time.Parse(format, (string)(timeStr)) + if e != nil { + t.Errorf("time string \"%s\" did not match provided time format \"%s\": %s", timeStr, format, e) + } + } + + checkTimeStr("2006-01-02T15:04:05.000000000Z07:00") + checkTimeStr("Mon Jan _2 15:04:05 2006") + checkTimeStr("") +} + // TODO add tests for sorting etc., this requires a parser for the text // formatter output. From 3c48581aebc1d427a8e8790287e09eb66004f95d Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 26 Mar 2015 22:48:30 -0400 Subject: [PATCH 005/547] changelog: add #158 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..566a6fbd9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# 0.7.2 + +formatter/text: Add configuration option for time format (#158) From 962ab2734442e3cd7ac6833c5dc6689d1532647e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Lafoucri=C3=A8re?= Date: Fri, 27 Mar 2015 09:43:24 -0400 Subject: [PATCH 006/547] Add graylog hook to Readme I have updated the list to be a table instead. It's much more readable than the previous version, and used in a lot of places (ex: https://github.com/codegangsta/negroni/blob/master/README.md) --- README.md | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index bf09541e8..f7b7aa926 100644 --- a/README.md +++ b/README.md @@ -189,31 +189,18 @@ func init() { } ``` -* [`github.com/Sirupsen/logrus/hooks/airbrake`](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go) - Send errors to an exception tracking service compatible with the Airbrake API. - Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. -* [`github.com/Sirupsen/logrus/hooks/papertrail`](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go) - Send errors to the Papertrail hosted logging service via UDP. - -* [`github.com/Sirupsen/logrus/hooks/syslog`](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) - Send errors to remote syslog server. - Uses standard library `log/syslog` behind the scenes. - -* [`github.com/Sirupsen/logrus/hooks/bugsnag`](https://github.com/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go) - Send errors to the Bugsnag exception tracking service. - -* [`github.com/nubo/hiprus`](https://github.com/nubo/hiprus) - Send errors to a channel in hipchat. - -* [`github.com/sebest/logrusly`](https://github.com/sebest/logrusly) - Send logs to Loggly (https://www.loggly.com/) - -* [`github.com/johntdyer/slackrus`](https://github.com/johntdyer/slackrus) - Hook for Slack chat. - -* [`github.com/wercker/journalhook`](https://github.com/wercker/journalhook). - Hook for logging to `systemd-journald`. +| Hook | Description | +| ----- | ----------- | +| [Airbrake](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go) | Send errors to an exception tracking service compatible with the Airbrake API. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. | +| [Papertrail](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go) | Send errors to the Papertrail 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/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go) | Send errors to the Bugsnag exception tracking 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-hooks/tree/master/graylog) | Hook for logging to [Graylog](http://graylog2.org/) | #### Level logging From f56d9074742da1b23fd305e0907ce37b2a08bd34 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 31 Mar 2015 11:24:31 -0400 Subject: [PATCH 007/547] readme: add example of context logging --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index f7b7aa926..be46da2c1 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,16 @@ func main() { "omg": true, "number": 100, }).Fatal("The ice breaks!") + + # A common pattern is to re-use fields between logging statements by re-using + # the logrus.Entry returned from WithFields() + contextLogger := log.WithFields(log.Fields{ + "common": "this is a common field", + "other": "I also should be logged always", + }) + + contextLogger.Info("I'll be logged with common and other field") + contextLogger.Info("Me too") } ``` From fc616a41c2ec6554a8e1787539efbdbcb50aac43 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 31 Mar 2015 11:30:41 -0400 Subject: [PATCH 008/547] readme: fix comment on context logger --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index be46da2c1..d55f90924 100644 --- a/README.md +++ b/README.md @@ -109,8 +109,8 @@ func main() { "number": 100, }).Fatal("The ice breaks!") - # A common pattern is to re-use fields between logging statements by re-using - # the logrus.Entry returned from WithFields() + // A common pattern is to re-use fields between logging statements by re-using + // the logrus.Entry returned from WithFields() contextLogger := log.WithFields(log.Fields{ "common": "this is a common field", "other": "I also should be logged always", From 230ac3a5777a299b1d422056a5a6034952390303 Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Fri, 3 Apr 2015 17:25:25 -0700 Subject: [PATCH 009/547] Allow configuration of timestamp layout Signed-off-by: Stephen J Day --- formatter.go | 4 ++++ formatters/logstash/logstash.go | 12 ++++++++++-- json_formatter.go | 13 ++++++++++--- text_formatter.go | 9 ++++----- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/formatter.go b/formatter.go index 038ce9fd2..104d689f1 100644 --- a/formatter.go +++ b/formatter.go @@ -1,5 +1,9 @@ package logrus +import "time" + +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/formatters/logstash/logstash.go b/formatters/logstash/logstash.go index 34b1ccbca..8ea93ddf2 100644 --- a/formatters/logstash/logstash.go +++ b/formatters/logstash/logstash.go @@ -3,19 +3,27 @@ package logstash import ( "encoding/json" "fmt" + "github.com/Sirupsen/logrus" - "time" ) // Formatter generates json in logstash format. // Logstash site: http://logstash.net/ type LogstashFormatter struct { Type string // if not empty use for logstash type field. + + // TimestampFormat sets the format used for timestamps. + TimestampFormat string } func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) { entry.Data["@version"] = 1 - entry.Data["@timestamp"] = entry.Time.Format(time.RFC3339) + + if f.TimestampFormat == "" { + f.TimestampFormat = logrus.DefaultTimestampFormat + } + + entry.Data["@timestamp"] = entry.Time.Format(f.TimestampFormat) // set message field v, ok := entry.Data["message"] diff --git a/json_formatter.go b/json_formatter.go index 5c4c44bbe..dcc4f1d9f 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -3,10 +3,12 @@ package logrus import ( "encoding/json" "fmt" - "time" ) -type JSONFormatter struct{} +type JSONFormatter struct { + // TimestampFormat sets the format used for marshaling timestamps. + TimestampFormat string +} func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { data := make(Fields, len(entry.Data)+3) @@ -21,7 +23,12 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { } } prefixFieldClashes(data) - data["time"] = entry.Time.Format(time.RFC3339) + + if f.TimestampFormat == "" { + f.TimestampFormat = DefaultTimestampFormat + } + + data["time"] = entry.Time.Format(f.TimestampFormat) data["msg"] = entry.Message data["level"] = entry.Level.String() diff --git a/text_formatter.go b/text_formatter.go index d3687ba25..612417ff9 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -18,9 +18,8 @@ const ( ) var ( - baseTimestamp time.Time - isTerminal bool - defaultTimestampFormat = time.RFC3339 + baseTimestamp time.Time + isTerminal bool ) func init() { @@ -47,7 +46,7 @@ type TextFormatter struct { // the time passed since beginning of execution. FullTimestamp bool - // Timestamp format to use for display, if a full timestamp is printed + // TimestampFormat to use for display when a full timestamp is printed TimestampFormat string // The fields are sorted by default for a consistent output. For applications @@ -73,7 +72,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { isColored := (f.ForceColors || isTerminal) && !f.DisableColors if f.TimestampFormat == "" { - f.TimestampFormat = defaultTimestampFormat + f.TimestampFormat = DefaultTimestampFormat } if isColored { f.printColored(b, entry, keys) From 45f05ae569ef8d846a1924a1a639a600a469d08d Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 9 Apr 2015 19:08:25 -0400 Subject: [PATCH 010/547] changelog: 0.7.3 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 566a6fbd9..eb72bff93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.7.3 + +formatter/\*: allow configuration of timestamp layout + # 0.7.2 formatter/text: Add configuration option for time format (#158) From b57a0a00ce46ee73104e62bcc18cdb71787bc32c Mon Sep 17 00:00:00 2001 From: Philip Allen Date: Fri, 17 Apr 2015 10:51:07 -0400 Subject: [PATCH 011/547] Added Raygun hook. --- hooks/raygun/raygun.go | 50 +++++++++++++++++++ hooks/raygun/raygun_test.go | 97 +++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 hooks/raygun/raygun.go create mode 100644 hooks/raygun/raygun_test.go diff --git a/hooks/raygun/raygun.go b/hooks/raygun/raygun.go new file mode 100644 index 000000000..ec1671970 --- /dev/null +++ b/hooks/raygun/raygun.go @@ -0,0 +1,50 @@ +package raygun + +import ( + "errors" + "net/http" + + "github.com/Sirupsen/logrus" + "github.com/sditools/goraygun" +) + +type raygunHook struct { + client *goraygun.Client +} + +func NewHook(Endpoint string, ApiKey string, Enabled bool) *raygunHook { + client := goraygun.Init(goraygun.Settings{ + ApiKey: ApiKey, + Endpoint: Endpoint, + Enabled: Enabled, + }, goraygun.Entry{}) + return &raygunHook{client} +} + +func (hook *raygunHook) Fire(logEntry *logrus.Entry) error { + // Start with a copy of the default entry + raygunEntry := hook.client.Entry + + if request, ok := logEntry.Data["request"]; ok { + raygunEntry.Details.Request.Populate(*(request.(*http.Request))) + } + + var reportErr error + if err, ok := logEntry.Data["error"]; ok { + reportErr = err.(error) + } else { + reportErr = errors.New(logEntry.Message) + } + + hook.client.Report(reportErr, raygunEntry) + + return nil +} + +func (hook *raygunHook) Levels() []logrus.Level { + return []logrus.Level{ + logrus.ErrorLevel, + logrus.FatalLevel, + logrus.PanicLevel, + } +} diff --git a/hooks/raygun/raygun_test.go b/hooks/raygun/raygun_test.go new file mode 100644 index 000000000..eda4b83d0 --- /dev/null +++ b/hooks/raygun/raygun_test.go @@ -0,0 +1,97 @@ +package raygun + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/Sirupsen/logrus" + "github.com/sditools/goraygun" +) + +type customErr struct { + msg string +} + +func (e *customErr) Error() string { + return e.msg +} + +const ( + testAPIKey = "abcxyz" + expectedClass = "github.com/Sirupsen/logrus/hooks/raygun" + expectedMsg = "oh no some error occured." + unintendedMsg = "Airbrake will not see this string" +) + +var ( + entryCh = make(chan goraygun.Entry, 1) +) + +// TestLogEntryMessageReceived checks if invoking Logrus' log.Error +// method causes an XML payload containing the log entry message is received +// by a HTTP server emulating an Airbrake-compatible endpoint. +func TestLogEntryMessageReceived(t *testing.T) { + log := logrus.New() + ts := startRaygunServer(t) + defer ts.Close() + + hook := NewHook(ts.URL, testAPIKey, true) + log.Hooks.Add(hook) + + log.Error(expectedMsg) + + select { + case received := <-entryCh: + if received.Details.Error.Message != expectedMsg { + t.Errorf("Unexpected message received: %s", received) + } + case <-time.After(time.Second): + t.Error("Timed out; no notice received by Raygun API") + } +} + +// TestLogEntryMessageReceived confirms that, when passing an error type using +// logrus.Fields, a HTTP server emulating an Airbrake endpoint receives the +// error message returned by the Error() method on the error interface +// rather than the logrus.Entry.Message string. +func TestLogEntryWithErrorReceived(t *testing.T) { + log := logrus.New() + ts := startRaygunServer(t) + defer ts.Close() + + hook := NewHook(ts.URL, testAPIKey, true) + log.Hooks.Add(hook) + + log.WithFields(logrus.Fields{ + "error": &customErr{expectedMsg}, + }).Error(unintendedMsg) + + select { + case received := <-entryCh: + if received.Details.Error.Message != expectedMsg { + t.Errorf("Unexpected message received: %s", received.Details.Error.Message) + } + if received.Details.Error.ClassName != expectedClass { + t.Errorf("Unexpected error class: %s", received.Details.Error.ClassName) + } + case <-time.After(time.Second): + t.Error("Timed out; no notice received by Airbrake API") + } +} + +func startRaygunServer(t *testing.T) *httptest.Server { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var entry goraygun.Entry + if err := json.NewDecoder(r.Body).Decode(&entry); err != nil { + t.Error(err) + } + r.Body.Close() + + entryCh <- entry + })) + + return ts +} From 6436f2aa2abfd865ff93bc43cd753aa927bced1c Mon Sep 17 00:00:00 2001 From: Antoine Grondin Date: Wed, 22 Apr 2015 22:48:32 -0400 Subject: [PATCH 012/547] assertify changed behavior and consider float64 != int --- logrus_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logrus_test.go b/logrus_test.go index d85dba4dc..efaacea23 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -191,7 +191,7 @@ func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) { log.WithField("level", 1).Info("test") }, func(fields Fields) { assert.Equal(t, fields["level"], "info") - assert.Equal(t, fields["fields.level"], 1) + assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only }) } From af0bd27a87348cbd7512f7867cbfb54bccc61dd0 Mon Sep 17 00:00:00 2001 From: Antoine Grondin Date: Wed, 22 Apr 2015 15:27:44 -0400 Subject: [PATCH 013/547] default logs to stderr --- CHANGELOG.md | 4 ++++ logger.go | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb72bff93..2cf1175cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.8 + +logrus: defaults to stderr instead of stdout + # 0.7.3 formatter/\*: allow configuration of timestamp layout diff --git a/logger.go b/logger.go index da928a375..3c07ea78c 100644 --- a/logger.go +++ b/logger.go @@ -44,7 +44,7 @@ type Logger struct { // It's recommended to make this a global instance called `log`. func New() *Logger { return &Logger{ - Out: os.Stdout, + Out: os.Stderr, Formatter: new(TextFormatter), Hooks: make(levelHooks), Level: InfoLevel, From eed86135dd4e1ff2cfbad24261a17cbbdeac12df Mon Sep 17 00:00:00 2001 From: Philip Allen Date: Fri, 1 May 2015 10:48:23 -0400 Subject: [PATCH 014/547] Moving raygun hook to its own repositiroy at github.com/squirkle/logrus-raygun-hook --- hooks/raygun/README.md | 18 +++++++ hooks/raygun/raygun.go | 50 ------------------- hooks/raygun/raygun_test.go | 97 ------------------------------------- 3 files changed, 18 insertions(+), 147 deletions(-) create mode 100644 hooks/raygun/README.md delete mode 100644 hooks/raygun/raygun.go delete mode 100644 hooks/raygun/raygun_test.go diff --git a/hooks/raygun/README.md b/hooks/raygun/README.md new file mode 100644 index 000000000..83e552b6f --- /dev/null +++ b/hooks/raygun/README.md @@ -0,0 +1,18 @@ +# [logrus-raygun-hook](https://github.com/squirkle/logrus-raygun-hook) +A Raygun.io hook for logrus + +## Usage + +```go +import ( + log "github.com/Sirupsen/logrus" + "github.com/squirkle/logrus-raygun-hook" +) + +func init() { + log.AddHook(raygun.NewHook("https://api.raygun.io/entries", "yourApiKey", true)) +} +``` + +## Project status +Both this logrus hook and the [goraygun](https://github.com/SDITools/goraygun) library are **works in progress**. Be aware of the possibility of upcoming improvements/API changes. diff --git a/hooks/raygun/raygun.go b/hooks/raygun/raygun.go deleted file mode 100644 index ec1671970..000000000 --- a/hooks/raygun/raygun.go +++ /dev/null @@ -1,50 +0,0 @@ -package raygun - -import ( - "errors" - "net/http" - - "github.com/Sirupsen/logrus" - "github.com/sditools/goraygun" -) - -type raygunHook struct { - client *goraygun.Client -} - -func NewHook(Endpoint string, ApiKey string, Enabled bool) *raygunHook { - client := goraygun.Init(goraygun.Settings{ - ApiKey: ApiKey, - Endpoint: Endpoint, - Enabled: Enabled, - }, goraygun.Entry{}) - return &raygunHook{client} -} - -func (hook *raygunHook) Fire(logEntry *logrus.Entry) error { - // Start with a copy of the default entry - raygunEntry := hook.client.Entry - - if request, ok := logEntry.Data["request"]; ok { - raygunEntry.Details.Request.Populate(*(request.(*http.Request))) - } - - var reportErr error - if err, ok := logEntry.Data["error"]; ok { - reportErr = err.(error) - } else { - reportErr = errors.New(logEntry.Message) - } - - hook.client.Report(reportErr, raygunEntry) - - return nil -} - -func (hook *raygunHook) Levels() []logrus.Level { - return []logrus.Level{ - logrus.ErrorLevel, - logrus.FatalLevel, - logrus.PanicLevel, - } -} diff --git a/hooks/raygun/raygun_test.go b/hooks/raygun/raygun_test.go deleted file mode 100644 index eda4b83d0..000000000 --- a/hooks/raygun/raygun_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package raygun - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/Sirupsen/logrus" - "github.com/sditools/goraygun" -) - -type customErr struct { - msg string -} - -func (e *customErr) Error() string { - return e.msg -} - -const ( - testAPIKey = "abcxyz" - expectedClass = "github.com/Sirupsen/logrus/hooks/raygun" - expectedMsg = "oh no some error occured." - unintendedMsg = "Airbrake will not see this string" -) - -var ( - entryCh = make(chan goraygun.Entry, 1) -) - -// TestLogEntryMessageReceived checks if invoking Logrus' log.Error -// method causes an XML payload containing the log entry message is received -// by a HTTP server emulating an Airbrake-compatible endpoint. -func TestLogEntryMessageReceived(t *testing.T) { - log := logrus.New() - ts := startRaygunServer(t) - defer ts.Close() - - hook := NewHook(ts.URL, testAPIKey, true) - log.Hooks.Add(hook) - - log.Error(expectedMsg) - - select { - case received := <-entryCh: - if received.Details.Error.Message != expectedMsg { - t.Errorf("Unexpected message received: %s", received) - } - case <-time.After(time.Second): - t.Error("Timed out; no notice received by Raygun API") - } -} - -// TestLogEntryMessageReceived confirms that, when passing an error type using -// logrus.Fields, a HTTP server emulating an Airbrake endpoint receives the -// error message returned by the Error() method on the error interface -// rather than the logrus.Entry.Message string. -func TestLogEntryWithErrorReceived(t *testing.T) { - log := logrus.New() - ts := startRaygunServer(t) - defer ts.Close() - - hook := NewHook(ts.URL, testAPIKey, true) - log.Hooks.Add(hook) - - log.WithFields(logrus.Fields{ - "error": &customErr{expectedMsg}, - }).Error(unintendedMsg) - - select { - case received := <-entryCh: - if received.Details.Error.Message != expectedMsg { - t.Errorf("Unexpected message received: %s", received.Details.Error.Message) - } - if received.Details.Error.ClassName != expectedClass { - t.Errorf("Unexpected error class: %s", received.Details.Error.ClassName) - } - case <-time.After(time.Second): - t.Error("Timed out; no notice received by Airbrake API") - } -} - -func startRaygunServer(t *testing.T) *httptest.Server { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var entry goraygun.Entry - if err := json.NewDecoder(r.Body).Decode(&entry); err != nil { - t.Error(err) - } - r.Body.Close() - - entryCh <- entry - })) - - return ts -} From b0ce94375fb534ffb1bb498d515e804f6e4ccf99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=20R=C3=B8dseth?= Date: Tue, 12 May 2015 16:31:17 +0200 Subject: [PATCH 015/547] Terminals on Windows may not have colors --- text_formatter.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text_formatter.go b/text_formatter.go index 612417ff9..4ed90e1e5 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -3,6 +3,7 @@ package logrus import ( "bytes" "fmt" + "runtime" "sort" "strings" "time" @@ -69,7 +70,8 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { prefixFieldClashes(entry.Data) - isColored := (f.ForceColors || isTerminal) && !f.DisableColors + isColorTerminal := isTerminal && (runtime.GOOS != "windows") + isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors if f.TimestampFormat == "" { f.TimestampFormat = DefaultTimestampFormat From 156efce8fe915b6fc928e7803e0f6f0c706274d2 Mon Sep 17 00:00:00 2001 From: Matthew Baird Date: Tue, 12 May 2015 09:27:20 -0700 Subject: [PATCH 016/547] proper use of TextFormatter in documentation --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d55f90924..64e4914db 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"} "time":"2014-03-10 19:57:38.562543128 -0400 EDT"} ``` -With the default `log.Formatter = new(logrus.TextFormatter)` when a TTY is not +With the default `log.Formatter = new(&log.TextFormatter{})` when a TTY is not attached, the output is compatible with the [logfmt](http://godoc.org/github.com/kr/logfmt) format: @@ -269,7 +269,7 @@ init() { log.SetFormatter(logrus.JSONFormatter) } else { // The TextFormatter is default, you don't actually have to do this. - log.SetFormatter(logrus.TextFormatter) + log.SetFormatter(&log.TextFormatter{}) } } ``` From 8db6c5ed121d4eb00c1da5b7249a147d6388b79d Mon Sep 17 00:00:00 2001 From: Joern Barthel Date: Wed, 13 May 2015 13:35:03 +0200 Subject: [PATCH 017/547] Added WithError(err). --- entry.go | 8 ++++++++ entry_test.go | 15 +++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/entry.go b/entry.go index 17fe6f707..b9206de48 100644 --- a/entry.go +++ b/entry.go @@ -8,6 +8,9 @@ import ( "time" ) +// Defines the key when adding error using WithError. +var ErrorKey = "error" + // An entry is the final or intermediate Logrus logging entry. It contains all // the fields passed with WithField{,s}. It's finally logged when Debug, Info, // Warn, Error, Fatal or Panic is called on it. These objects can be reused and @@ -53,6 +56,11 @@ func (entry *Entry) String() (string, error) { return reader.String(), err } +// Add an error as single field (with key "error") to the Entry. +func (entry *Entry) WithError(err error) *Entry { + return entry.WithField(ErrorKey, err) +} + // Add a single field to the Entry. func (entry *Entry) WithField(key string, value interface{}) *Entry { return entry.WithFields(Fields{key: value}) diff --git a/entry_test.go b/entry_test.go index 98717df49..a824e6405 100644 --- a/entry_test.go +++ b/entry_test.go @@ -8,6 +8,21 @@ import ( "github.com/stretchr/testify/assert" ) +func TestEntryWithError(t *testing.T) { + + err := fmt.Errorf("kaboom at layer %d", 4711) + + logger := New() + logger.Out = &bytes.Buffer{} + entry := NewEntry(logger) + + assert.Equal(t, err, entry.WithError(err).Data["error"]) + + ErrorKey = "err" + assert.Equal(t, err, entry.WithError(err).Data["err"]) + +} + func TestEntryPanicln(t *testing.T) { errBoom := fmt.Errorf("boom time") From a526ac0506f16b34454d792279ba8b3f27342348 Mon Sep 17 00:00:00 2001 From: Joern Barthel Date: Tue, 19 May 2015 19:50:55 +0200 Subject: [PATCH 018/547] Implement WithError(err) in exported, fixed doco. --- entry.go | 4 ++-- entry_test.go | 13 +++++++++++-- exported.go | 5 +++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/entry.go b/entry.go index b9206de48..c0f398f6f 100644 --- a/entry.go +++ b/entry.go @@ -8,7 +8,7 @@ import ( "time" ) -// Defines the key when adding error using WithError. +// Defines the key when adding errors using WithError. var ErrorKey = "error" // An entry is the final or intermediate Logrus logging entry. It contains all @@ -56,7 +56,7 @@ func (entry *Entry) String() (string, error) { return reader.String(), err } -// Add an error as single field (with key "error") to the Entry. +// Add an error as single field (using the key defined in ErrorKey) to the Entry. func (entry *Entry) WithError(err error) *Entry { return entry.WithField(ErrorKey, err) } diff --git a/entry_test.go b/entry_test.go index a824e6405..99c3b41d5 100644 --- a/entry_test.go +++ b/entry_test.go @@ -10,16 +10,25 @@ import ( func TestEntryWithError(t *testing.T) { + assert := assert.New(t) + + defer func() { + ErrorKey = "error" + }() + err := fmt.Errorf("kaboom at layer %d", 4711) + assert.Equal(err, WithError(err).Data["error"]) + logger := New() logger.Out = &bytes.Buffer{} entry := NewEntry(logger) - assert.Equal(t, err, entry.WithError(err).Data["error"]) + assert.Equal(err, entry.WithError(err).Data["error"]) ErrorKey = "err" - assert.Equal(t, err, entry.WithError(err).Data["err"]) + + assert.Equal(err, entry.WithError(err).Data["err"]) } diff --git a/exported.go b/exported.go index a67e1b802..9a0120ac1 100644 --- a/exported.go +++ b/exported.go @@ -48,6 +48,11 @@ func AddHook(hook Hook) { std.Hooks.Add(hook) } +// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key. +func WithError(err error) *Entry { + return std.WithField(ErrorKey, err) +} + // WithField creates an entry from the standard logger and adds a field to // it. If you want multiple fields, use `WithFields`. // From dd3f39332d1aeaa3432648cf9790673812cb2828 Mon Sep 17 00:00:00 2001 From: Joern Barthel Date: Tue, 19 May 2015 20:20:59 +0200 Subject: [PATCH 019/547] Implement casting of *Entry to error. --- entry.go | 68 +++++++++++++++++++++++++++++++++------------------ entry_test.go | 19 ++++++++++++-- 2 files changed, 61 insertions(+), 26 deletions(-) diff --git a/entry.go b/entry.go index c0f398f6f..b8889f44d 100644 --- a/entry.go +++ b/entry.go @@ -56,6 +56,14 @@ func (entry *Entry) String() (string, error) { return reader.String(), err } +// ToError returns the field value of ErrorKey (nil) +func (entry *Entry) ToError() error { + if err, ok := entry.Data[ErrorKey].(error); ok { + return err + } + return nil +} + // Add an error as single field (using the key defined in ErrorKey) to the Entry. func (entry *Entry) WithError(err error) *Entry { return entry.WithField(ErrorKey, err) @@ -112,36 +120,40 @@ func (entry *Entry) log(level Level, msg string) { } } -func (entry *Entry) Debug(args ...interface{}) { +func (entry *Entry) Debug(args ...interface{}) *Entry { if entry.Logger.Level >= DebugLevel { entry.log(DebugLevel, fmt.Sprint(args...)) } + return entry } -func (entry *Entry) Print(args ...interface{}) { - entry.Info(args...) +func (entry *Entry) Print(args ...interface{}) *Entry { + return entry.Info(args...) } -func (entry *Entry) Info(args ...interface{}) { +func (entry *Entry) Info(args ...interface{}) *Entry { if entry.Logger.Level >= InfoLevel { entry.log(InfoLevel, fmt.Sprint(args...)) } + return entry } -func (entry *Entry) Warn(args ...interface{}) { +func (entry *Entry) Warn(args ...interface{}) *Entry { if entry.Logger.Level >= WarnLevel { entry.log(WarnLevel, fmt.Sprint(args...)) } + return entry } -func (entry *Entry) Warning(args ...interface{}) { - entry.Warn(args...) +func (entry *Entry) Warning(args ...interface{}) *Entry { + return entry.Warn(args...) } -func (entry *Entry) Error(args ...interface{}) { +func (entry *Entry) Error(args ...interface{}) *Entry { if entry.Logger.Level >= ErrorLevel { entry.log(ErrorLevel, fmt.Sprint(args...)) } + return entry } func (entry *Entry) Fatal(args ...interface{}) { @@ -160,36 +172,40 @@ func (entry *Entry) Panic(args ...interface{}) { // Entry Printf family functions -func (entry *Entry) Debugf(format string, args ...interface{}) { +func (entry *Entry) Debugf(format string, args ...interface{}) *Entry { if entry.Logger.Level >= DebugLevel { entry.Debug(fmt.Sprintf(format, args...)) } + return entry } -func (entry *Entry) Infof(format string, args ...interface{}) { +func (entry *Entry) Infof(format string, args ...interface{}) *Entry { if entry.Logger.Level >= InfoLevel { entry.Info(fmt.Sprintf(format, args...)) } + return entry } -func (entry *Entry) Printf(format string, args ...interface{}) { - entry.Infof(format, args...) +func (entry *Entry) Printf(format string, args ...interface{}) *Entry { + return entry.Infof(format, args...) } -func (entry *Entry) Warnf(format string, args ...interface{}) { +func (entry *Entry) Warnf(format string, args ...interface{}) *Entry { if entry.Logger.Level >= WarnLevel { entry.Warn(fmt.Sprintf(format, args...)) } + return entry } -func (entry *Entry) Warningf(format string, args ...interface{}) { - entry.Warnf(format, args...) +func (entry *Entry) Warningf(format string, args ...interface{}) *Entry { + return entry.Warnf(format, args...) } -func (entry *Entry) Errorf(format string, args ...interface{}) { +func (entry *Entry) Errorf(format string, args ...interface{}) *Entry { if entry.Logger.Level >= ErrorLevel { entry.Error(fmt.Sprintf(format, args...)) } + return entry } func (entry *Entry) Fatalf(format string, args ...interface{}) { @@ -206,36 +222,40 @@ func (entry *Entry) Panicf(format string, args ...interface{}) { // Entry Println family functions -func (entry *Entry) Debugln(args ...interface{}) { +func (entry *Entry) Debugln(args ...interface{}) *Entry { if entry.Logger.Level >= DebugLevel { entry.Debug(entry.sprintlnn(args...)) } + return entry } -func (entry *Entry) Infoln(args ...interface{}) { +func (entry *Entry) Infoln(args ...interface{}) *Entry { if entry.Logger.Level >= InfoLevel { entry.Info(entry.sprintlnn(args...)) } + return entry } -func (entry *Entry) Println(args ...interface{}) { - entry.Infoln(args...) +func (entry *Entry) Println(args ...interface{}) *Entry { + return entry.Infoln(args...) } -func (entry *Entry) Warnln(args ...interface{}) { +func (entry *Entry) Warnln(args ...interface{}) *Entry { if entry.Logger.Level >= WarnLevel { entry.Warn(entry.sprintlnn(args...)) } + return entry } -func (entry *Entry) Warningln(args ...interface{}) { - entry.Warnln(args...) +func (entry *Entry) Warningln(args ...interface{}) *Entry { + return entry.Warnln(args...) } -func (entry *Entry) Errorln(args ...interface{}) { +func (entry *Entry) Errorln(args ...interface{}) *Entry { if entry.Logger.Level >= ErrorLevel { entry.Error(entry.sprintlnn(args...)) } + return entry } func (entry *Entry) Fatalln(args ...interface{}) { diff --git a/entry_test.go b/entry_test.go index 99c3b41d5..3473ffb66 100644 --- a/entry_test.go +++ b/entry_test.go @@ -8,6 +8,23 @@ import ( "github.com/stretchr/testify/assert" ) +var err = fmt.Errorf("kaboom at layer %d", 4711) + +func TestToError(t *testing.T) { + + assert := assert.New(t) + + ctx := WithField("foo", "bar") + assert.Equal(nil, ctx.Debug("Hello").ToError()) + + ctx.Data[ErrorKey] = "error" + assert.Equal(nil, ctx.Debug("Hello").ToError()) + + ctx = ctx.WithError(err) + assert.Equal(err, ctx.Debug("Hello").ToError()) + +} + func TestEntryWithError(t *testing.T) { assert := assert.New(t) @@ -16,8 +33,6 @@ func TestEntryWithError(t *testing.T) { ErrorKey = "error" }() - err := fmt.Errorf("kaboom at layer %d", 4711) - assert.Equal(err, WithError(err).Data["error"]) logger := New() From 8d7db8e720a31e2d3b4e6a5b4a032ce202666001 Mon Sep 17 00:00:00 2001 From: Philip Allen Date: Thu, 21 May 2015 10:28:14 -0400 Subject: [PATCH 020/547] removing raygun hook from hooks dir, adding reference in hooks table of main README.md --- README.md | 1 + hooks/raygun/README.md | 18 ------------------ 2 files changed, 1 insertion(+), 18 deletions(-) delete mode 100644 hooks/raygun/README.md diff --git a/README.md b/README.md index 64e4914db..3578deaec 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,7 @@ func init() { | [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-hooks/tree/master/graylog) | 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/) | #### Level logging diff --git a/hooks/raygun/README.md b/hooks/raygun/README.md deleted file mode 100644 index 83e552b6f..000000000 --- a/hooks/raygun/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# [logrus-raygun-hook](https://github.com/squirkle/logrus-raygun-hook) -A Raygun.io hook for logrus - -## Usage - -```go -import ( - log "github.com/Sirupsen/logrus" - "github.com/squirkle/logrus-raygun-hook" -) - -func init() { - log.AddHook(raygun.NewHook("https://api.raygun.io/entries", "yourApiKey", true)) -} -``` - -## Project status -Both this logrus hook and the [goraygun](https://github.com/SDITools/goraygun) library are **works in progress**. Be aware of the possibility of upcoming improvements/API changes. From 1b6b9583ee44bf8d3b2a7bc9167331af3dae29d6 Mon Sep 17 00:00:00 2001 From: evalphobia Date: Fri, 22 May 2015 21:14:51 +0900 Subject: [PATCH 021/547] Added special field for *http.Request to Sentry hook --- hooks/sentry/README.md | 7 ++++--- hooks/sentry/sentry.go | 22 +++++++++++++++++++++- hooks/sentry/sentry_test.go | 7 +++++-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/hooks/sentry/README.md b/hooks/sentry/README.md index 19e58bb45..4e1c1476f 100644 --- a/hooks/sentry/README.md +++ b/hooks/sentry/README.md @@ -34,12 +34,13 @@ func main() { ## Special fields Some logrus fields have a special meaning in this hook, -these are server_name and logger. +these are `server_name`, `logger` and `http_request`. When logs are sent to sentry these fields are treated differently. -- server_name (also known as hostname) is the name of the server which +- `server_name` (also known as hostname) is the name of the server which is logging the event (hostname.example.com) -- logger is the part of the application which is logging the event. +- `logger` is the part of the application which is logging the event. In go this usually means setting it to the name of the package. +- `http_request` is the in-coming request(*http.Request). The detailed request data are sent to Sentry. ## Timeout diff --git a/hooks/sentry/sentry.go b/hooks/sentry/sentry.go index 379f281c5..e7e45b21b 100644 --- a/hooks/sentry/sentry.go +++ b/hooks/sentry/sentry.go @@ -3,6 +3,7 @@ package logrus_sentry import ( "fmt" "time" + "net/http" "github.com/Sirupsen/logrus" "github.com/getsentry/raven-go" @@ -36,6 +37,22 @@ func getAndDel(d logrus.Fields, key string) (string, bool) { return val, true } +func getAndDelRequest(d logrus.Fields, key string) (*http.Request, bool) { + var ( + ok bool + v interface{} + req *http.Request + ) + if v, ok = d[key]; !ok { + return nil, false + } + if req, ok = v.(*http.Request); !ok || req == nil { + return nil, false + } + delete(d, key) + return req, true +} + // SentryHook delivers logs to a sentry server. type SentryHook struct { // Timeout sets the time to wait for a delivery error from the sentry server. @@ -61,7 +78,7 @@ func NewSentryHook(DSN string, levels []logrus.Level) (*SentryHook, error) { // Called when an event should be sent to sentry // Special fields that sentry uses to give more information to the server // are extracted from entry.Data (if they are found) -// These fields are: logger and server_name +// These fields are: logger, server_name and http_request func (hook *SentryHook) Fire(entry *logrus.Entry) error { packet := &raven.Packet{ Message: entry.Message, @@ -78,6 +95,9 @@ func (hook *SentryHook) Fire(entry *logrus.Entry) error { if serverName, ok := getAndDel(d, "server_name"); ok { packet.ServerName = serverName } + if req, ok := getAndDelRequest(d, "http_request"); ok { + packet.Interfaces = append(packet.Interfaces, raven.NewHttp(req)) + } packet.Extra = map[string]interface{}(d) _, errCh := hook.client.Capture(packet, nil) diff --git a/hooks/sentry/sentry_test.go b/hooks/sentry/sentry_test.go index 45f18d170..5f3696b29 100644 --- a/hooks/sentry/sentry_test.go +++ b/hooks/sentry/sentry_test.go @@ -61,9 +61,12 @@ func TestSpecialFields(t *testing.T) { t.Fatal(err.Error()) } logger.Hooks.Add(hook) + + req, _ := http.NewRequest("GET", "url", nil) logger.WithFields(logrus.Fields{ - "server_name": server_name, - "logger": logger_name, + "server_name": server_name, + "logger": logger_name, + "http_request": req, }).Error(message) packet := <-pch From 4c7ddd50c1430ca2f072162b6ef3986f51d19127 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 26 May 2015 09:02:13 -0400 Subject: [PATCH 022/547] formatter/json: fix possible race --- json_formatter.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/json_formatter.go b/json_formatter.go index dcc4f1d9f..2ad6dc5cf 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -24,11 +24,12 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { } prefixFieldClashes(data) - if f.TimestampFormat == "" { - f.TimestampFormat = DefaultTimestampFormat + timestampFormat := f.TimestampFormat + if timestampFormat == "" { + timestampFormat = DefaultTimestampFormat } - data["time"] = entry.Time.Format(f.TimestampFormat) + data["time"] = entry.Time.Format(timestampFormat) data["msg"] = entry.Message data["level"] = entry.Level.String() From d33cd5ce1918c46e9fab02dbddb9c39b1c127f7d Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 26 May 2015 09:04:23 -0400 Subject: [PATCH 023/547] version: bump to 0.8 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cf1175cd..f588601f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # 0.8 logrus: defaults to stderr instead of stdout +hooks/sentry: add special field for `*http.Request` +formatter/text: ignore Windows for colors # 0.7.3 From 86991062cb3793fd5978c5301a6880e8c3f701ff Mon Sep 17 00:00:00 2001 From: Madhav Puri Date: Tue, 26 May 2015 14:00:11 -0700 Subject: [PATCH 024/547] Fix Fatalf() and Fatalln() to exit irrespective of log level Signed-off-by: Madhav Puri --- entry.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/entry.go b/entry.go index 17fe6f707..699ea035c 100644 --- a/entry.go +++ b/entry.go @@ -188,6 +188,7 @@ func (entry *Entry) Fatalf(format string, args ...interface{}) { if entry.Logger.Level >= FatalLevel { entry.Fatal(fmt.Sprintf(format, args...)) } + os.Exit(1) } func (entry *Entry) Panicf(format string, args ...interface{}) { @@ -234,6 +235,7 @@ func (entry *Entry) Fatalln(args ...interface{}) { if entry.Logger.Level >= FatalLevel { entry.Fatal(entry.sprintlnn(args...)) } + os.Exit(1) } func (entry *Entry) Panicln(args ...interface{}) { From f4d77854cf5e8897b5863be40fe03a3e161b2d55 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 26 May 2015 18:46:05 -0400 Subject: [PATCH 025/547] version: bump to 0.8.1 --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f588601f9..ba3de2396 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ -# 0.8 +# 0.8.1 + +logrus: fix not exiting on `Fatalf` and `Fatalln` + +# 0.8.0 logrus: defaults to stderr instead of stdout hooks/sentry: add special field for `*http.Request` From ef773967044cbd094d902d7d9dd88c4bf82f77d2 Mon Sep 17 00:00:00 2001 From: Madhav Puri Date: Tue, 26 May 2015 16:42:49 -0700 Subject: [PATCH 026/547] Fix Fatal*() function of logger to match the behavior of Fatal*() functions of entry Signed-off-by: Madhav Puri --- logger.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/logger.go b/logger.go index 3c07ea78c..292b0b2c0 100644 --- a/logger.go +++ b/logger.go @@ -102,6 +102,7 @@ func (logger *Logger) Fatalf(format string, args ...interface{}) { if logger.Level >= FatalLevel { NewEntry(logger).Fatalf(format, args...) } + os.Exit(1) } func (logger *Logger) Panicf(format string, args ...interface{}) { @@ -148,6 +149,7 @@ func (logger *Logger) Fatal(args ...interface{}) { if logger.Level >= FatalLevel { NewEntry(logger).Fatal(args...) } + os.Exit(1) } func (logger *Logger) Panic(args ...interface{}) { @@ -194,6 +196,7 @@ func (logger *Logger) Fatalln(args ...interface{}) { if logger.Level >= FatalLevel { NewEntry(logger).Fatalln(args...) } + os.Exit(1) } func (logger *Logger) Panicln(args ...interface{}) { From f7a3a739cdbc1528d8a70f3bbad9c0983fb4fbee Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 26 May 2015 20:10:50 -0400 Subject: [PATCH 027/547] version: 0.8.2 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba3de2396..49c5506a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.8.2 + +logrus: fix more Fatal family functions + # 0.8.1 logrus: fix not exiting on `Fatalf` and `Fatalln` From 13a50f0eaaf37b3b1b19053d42891297de0d58c7 Mon Sep 17 00:00:00 2001 From: "Dotan J. Nahum" Date: Wed, 27 May 2015 14:29:11 +0300 Subject: [PATCH 028/547] logrus_syslog / syslog - example should now be valid --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3578deaec..c963c7d4d 100644 --- a/README.md +++ b/README.md @@ -183,7 +183,7 @@ Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in import ( log "github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus/hooks/airbrake" - "github.com/Sirupsen/logrus/hooks/syslog" + logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" "log/syslog" ) From ec0fdc740927c845719beaa44c4314c8c6da05ed Mon Sep 17 00:00:00 2001 From: "Rebill.Ruan" Date: Tue, 2 Jun 2015 15:33:32 +0800 Subject: [PATCH 029/547] fix error --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c963c7d4d..77e5fcb4f 100644 --- a/README.md +++ b/README.md @@ -267,7 +267,7 @@ init() { // do something here to set environment depending on an environment variable // or command-line flag if Environment == "production" { - log.SetFormatter(logrus.JSONFormatter) + log.SetFormatter(&logrus.JSONFormatter{}) } else { // The TextFormatter is default, you don't actually have to do this. log.SetFormatter(&log.TextFormatter{}) From f563ea38e989ea4d1fe4e0ae6e182737d0adf4e3 Mon Sep 17 00:00:00 2001 From: rifflock Date: Tue, 2 Jun 2015 15:36:57 -0700 Subject: [PATCH 030/547] Include lfshook in list of available hooks --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 77e5fcb4f..8e9166755 100644 --- a/README.md +++ b/README.md @@ -212,6 +212,7 @@ func init() { | [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` | | [Graylog](https://github.com/gemnasium/logrus-hooks/tree/master/graylog) | 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 | #### Level logging From 1b025712b862348a5c895d8258e351425afdc380 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 8 Jun 2015 19:54:53 -0400 Subject: [PATCH 031/547] readme: add @agonzalezro honeybadger hook to readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8e9166755..521231979 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,8 @@ func init() { | [Graylog](https://github.com/gemnasium/logrus-hooks/tree/master/graylog) | 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 | #### Level logging From 17ae28065fac0b74210c1240efb98c31c857c0f7 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 8 Jun 2015 19:55:33 -0400 Subject: [PATCH 032/547] readme: fix typo in honeybadger --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 521231979..f3f11d993 100644 --- a/README.md +++ b/README.md @@ -213,8 +213,7 @@ func init() { | [Graylog](https://github.com/gemnasium/logrus-hooks/tree/master/graylog) | 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 | +| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger | #### Level logging From bb6f952b7b8390299ea650d6c228a8cbf94c199e Mon Sep 17 00:00:00 2001 From: Rene Zbinden Date: Wed, 10 Jun 2015 11:09:59 +0200 Subject: [PATCH 033/547] add link to mail hook --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f3f11d993..cb73b0180 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,7 @@ func init() { | [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 | #### Level logging From d3e7a1335ac22a308d11ae41d5ab8835f050738e Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Fri, 19 Jun 2015 16:04:35 -0700 Subject: [PATCH 034/547] Link to the rollbar notification hook that we use. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index cb73b0180..938810aea 100644 --- a/README.md +++ b/README.md @@ -215,6 +215,7 @@ func init() { | [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 | #### Level logging From b868e7bb2309ccbacbbc4b07b057e66d99572e27 Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Wed, 24 Jun 2015 19:37:01 -0700 Subject: [PATCH 035/547] Added link to Sentry hook. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 938810aea..bae5a9f98 100644 --- a/README.md +++ b/README.md @@ -206,6 +206,7 @@ func init() { | [Papertrail](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go) | Send errors to the Papertrail 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/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go) | Send errors to the Bugsnag exception tracking service. | +| [Sentry](https://github.com/Sirupsen/logrus/blob/master/hooks/sentry/sentry.go) | 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. | From 2e5a78b0203a4bac02a4d559c7deb07a7224aa54 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Fri, 26 Jun 2015 11:42:49 -0400 Subject: [PATCH 036/547] hooks: expose LevelHooks type --- hooks.go | 6 +++--- logger.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hooks.go b/hooks.go index 0da2b3653..3f151cdc3 100644 --- a/hooks.go +++ b/hooks.go @@ -11,11 +11,11 @@ type Hook interface { } // Internal type for storing the hooks on a logger instance. -type levelHooks map[Level][]Hook +type LevelHooks map[Level][]Hook // Add a hook to an instance of logger. This is called with // `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface. -func (hooks levelHooks) Add(hook Hook) { +func (hooks LevelHooks) Add(hook Hook) { for _, level := range hook.Levels() { hooks[level] = append(hooks[level], hook) } @@ -23,7 +23,7 @@ func (hooks levelHooks) Add(hook Hook) { // Fire all the hooks for the passed level. Used by `entry.log` to fire // appropriate hooks for a log entry. -func (hooks levelHooks) Fire(level Level, entry *Entry) error { +func (hooks LevelHooks) Fire(level Level, entry *Entry) error { for _, hook := range hooks[level] { if err := hook.Fire(entry); err != nil { return err diff --git a/logger.go b/logger.go index 292b0b2c0..e4974bfbe 100644 --- a/logger.go +++ b/logger.go @@ -14,7 +14,7 @@ type Logger struct { // Hooks for the logger instance. These allow firing events based on logging // levels and log entries. For example, to send errors to an error tracking // service, log to StatsD or dump the core on fatal errors. - Hooks levelHooks + Hooks LevelHooks // All log entries pass through the formatter before logged to Out. The // included formatters are `TextFormatter` and `JSONFormatter` for which // TextFormatter is the default. In development (when a TTY is attached) it @@ -37,7 +37,7 @@ type Logger struct { // var log = &Logger{ // Out: os.Stderr, // Formatter: new(JSONFormatter), -// Hooks: make(levelHooks), +// Hooks: make(LevelHooks), // Level: logrus.DebugLevel, // } // @@ -46,7 +46,7 @@ func New() *Logger { return &Logger{ Out: os.Stderr, Formatter: new(TextFormatter), - Hooks: make(levelHooks), + Hooks: make(LevelHooks), Level: InfoLevel, } } From 351b006cc99fc3ffcd7c97399541e63b2f35e8ff Mon Sep 17 00:00:00 2001 From: Curtis Gagliardi Date: Fri, 26 Jun 2015 12:27:29 -0700 Subject: [PATCH 037/547] Add support for DragonflyBSD and NetBSD. This merges all the bsd (including darwin) support into terminal_bsd.go Cross-compiling works for all of them in that they all compile, but I've only actually tested the binary on dragonflybsd. I don't see why this wouldn't work on the others since I don't think they'd compile if they didn't support TIOCGETA and Termios, but just a heads up. --- terminal_openbsd.go => terminal_bsd.go | 2 ++ terminal_darwin.go | 12 ------------ terminal_freebsd.go | 20 -------------------- terminal_notwindows.go | 2 +- 4 files changed, 3 insertions(+), 33 deletions(-) rename terminal_openbsd.go => terminal_bsd.go (67%) delete mode 100644 terminal_darwin.go delete mode 100644 terminal_freebsd.go diff --git a/terminal_openbsd.go b/terminal_bsd.go similarity index 67% rename from terminal_openbsd.go rename to terminal_bsd.go index af609a53d..71f8d67a5 100644 --- a/terminal_openbsd.go +++ b/terminal_bsd.go @@ -1,3 +1,5 @@ +// +build darwin freebsd openbsd netbsd dragonfly + package logrus import "syscall" diff --git a/terminal_darwin.go b/terminal_darwin.go deleted file mode 100644 index 8fe02a4ae..000000000 --- a/terminal_darwin.go +++ /dev/null @@ -1,12 +0,0 @@ -// Based on ssh/terminal: -// Copyright 2013 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. - -package logrus - -import "syscall" - -const ioctlReadTermios = syscall.TIOCGETA - -type Termios syscall.Termios diff --git a/terminal_freebsd.go b/terminal_freebsd.go deleted file mode 100644 index 0428ee5d5..000000000 --- a/terminal_freebsd.go +++ /dev/null @@ -1,20 +0,0 @@ -/* - Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin. -*/ -package logrus - -import ( - "syscall" -) - -const ioctlReadTermios = syscall.TIOCGETA - -type Termios struct { - Iflag uint32 - Oflag uint32 - Cflag uint32 - Lflag uint32 - Cc [20]uint8 - Ispeed uint32 - Ospeed uint32 -} diff --git a/terminal_notwindows.go b/terminal_notwindows.go index b8bebc13e..4bb537602 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 +// +build linux darwin freebsd openbsd netbsd dragonfly package logrus From 3da9cda26a08030f74d8aa29983ee872a0dbf97a Mon Sep 17 00:00:00 2001 From: Anton Tiurin Date: Sat, 27 Jun 2015 17:09:22 +0300 Subject: [PATCH 038/547] [TextFormatter] Speed up. Fprintf is changed to buffer.Write* As Fprintf is slower than buffer.WriteString, it's replaced to faster call. Signed-off-by: Anton Tiurin --- formatter_bench_test.go | 10 ++++++++++ text_formatter.go | 25 ++++++++++++++++--------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/formatter_bench_test.go b/formatter_bench_test.go index 77989da62..c6d290c77 100644 --- a/formatter_bench_test.go +++ b/formatter_bench_test.go @@ -1,6 +1,7 @@ package logrus import ( + "fmt" "testing" "time" ) @@ -45,6 +46,15 @@ var largeFields = Fields{ "entries": "yeah", } +var errorFields = Fields{ + "foo": fmt.Errorf("bar"), + "baz": fmt.Errorf("qux"), +} + +func BenchmarkErrorTextFormatter(b *testing.B) { + doBenchmark(b, &TextFormatter{DisableColors: true}, errorFields) +} + func BenchmarkSmallTextFormatter(b *testing.B) { doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields) } diff --git a/text_formatter.go b/text_formatter.go index 4ed90e1e5..2e6fe1bdd 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -131,21 +131,28 @@ func needsQuoting(text string) bool { return true } -func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) { - switch value.(type) { +func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { + + b.WriteString(key) + b.WriteByte('=') + + switch value := value.(type) { case string: - if needsQuoting(value.(string)) { - fmt.Fprintf(b, "%v=%s ", key, value) + if needsQuoting(value) { + b.WriteString(value) } else { - fmt.Fprintf(b, "%v=%q ", key, value) + fmt.Fprintf(b, "%q", value) } case error: - if needsQuoting(value.(error).Error()) { - fmt.Fprintf(b, "%v=%s ", key, value) + errmsg := value.Error() + if needsQuoting(errmsg) { + b.WriteString(errmsg) } else { - fmt.Fprintf(b, "%v=%q ", key, value) + fmt.Fprintf(b, "%q", value) } default: - fmt.Fprintf(b, "%v=%v ", key, value) + fmt.Fprint(b, value) } + + b.WriteByte(' ') } From b632c0d3e83b51ae10c4642663a82223e517e924 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 30 Jun 2015 11:11:41 -0400 Subject: [PATCH 039/547] changelog: add entries for unreleased --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49c5506a3..a38715497 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# (Unreleased) + +logrus/core: improve performance of text formatter by 40% +logrus/core: expose `LevelHooks` type + # 0.8.2 logrus: fix more Fatal family functions From 95653e6ff9195ce394840c2f5d5041b392551c19 Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Tue, 30 Jun 2015 23:20:00 +0200 Subject: [PATCH 040/547] fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bae5a9f98..4be378476 100644 --- a/README.md +++ b/README.md @@ -329,7 +329,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { #### Logger as an `io.Writer` -Logrus can be transormed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it. +Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it. ```go w := logger.Writer() From 0c3a3b7e01125be7e93238b389e25f593e382fe9 Mon Sep 17 00:00:00 2001 From: Peter Fern Date: Sun, 5 Jul 2015 17:06:52 +1000 Subject: [PATCH 041/547] Print struct keys for struct log fields Can be useful for debugging, saves a lot of referring to source code --- text_formatter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text_formatter.go b/text_formatter.go index 2e6fe1bdd..e25f86cdd 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -115,7 +115,7 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin } for _, k := range keys { v := entry.Data[k] - fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v) + fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v) } } From 081376046542a22336972a1b51b2ee3105acdc06 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 6 Jul 2015 11:44:55 -0400 Subject: [PATCH 042/547] changelog: add BSD note --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a38715497..e51ea37e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ logrus/core: improve performance of text formatter by 40% logrus/core: expose `LevelHooks` type +logrus/core: add support for DragonflyBSD and NetBSD # 0.8.2 From 2dd349e522c0fb5be1b747478e9b8b959ca277e1 Mon Sep 17 00:00:00 2001 From: Arkan Date: Mon, 6 Jul 2015 22:27:39 +0200 Subject: [PATCH 043/547] Fix Entry log level --- CHANGELOG.md | 1 + entry.go | 41 +++++++++++++++++++++-------------------- entry_test.go | 14 ++++++++++++++ 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a38715497..ca482d9b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # (Unreleased) +logrus: fix entry log level (#208) logrus/core: improve performance of text formatter by 40% logrus/core: expose `LevelHooks` type diff --git a/entry.go b/entry.go index 699ea035c..2a980651e 100644 --- a/entry.go +++ b/entry.go @@ -32,7 +32,8 @@ func NewEntry(logger *Logger) *Entry { return &Entry{ Logger: logger, // Default is three fields, give a little extra room - Data: make(Fields, 5), + Data: make(Fields, 5), + Level: logger.Level, } } @@ -67,7 +68,7 @@ func (entry *Entry) WithFields(fields Fields) *Entry { for k, v := range fields { data[k] = v } - return &Entry{Logger: entry.Logger, Data: data} + return &Entry{Logger: entry.Logger, Data: data, Level: entry.Level} } func (entry *Entry) log(level Level, msg string) { @@ -105,7 +106,7 @@ func (entry *Entry) log(level Level, msg string) { } func (entry *Entry) Debug(args ...interface{}) { - if entry.Logger.Level >= DebugLevel { + if entry.Level >= DebugLevel { entry.log(DebugLevel, fmt.Sprint(args...)) } } @@ -115,13 +116,13 @@ func (entry *Entry) Print(args ...interface{}) { } func (entry *Entry) Info(args ...interface{}) { - if entry.Logger.Level >= InfoLevel { + if entry.Level >= InfoLevel { entry.log(InfoLevel, fmt.Sprint(args...)) } } func (entry *Entry) Warn(args ...interface{}) { - if entry.Logger.Level >= WarnLevel { + if entry.Level >= WarnLevel { entry.log(WarnLevel, fmt.Sprint(args...)) } } @@ -131,20 +132,20 @@ func (entry *Entry) Warning(args ...interface{}) { } func (entry *Entry) Error(args ...interface{}) { - if entry.Logger.Level >= ErrorLevel { + if entry.Level >= ErrorLevel { entry.log(ErrorLevel, fmt.Sprint(args...)) } } func (entry *Entry) Fatal(args ...interface{}) { - if entry.Logger.Level >= FatalLevel { + if entry.Level >= FatalLevel { entry.log(FatalLevel, fmt.Sprint(args...)) } os.Exit(1) } func (entry *Entry) Panic(args ...interface{}) { - if entry.Logger.Level >= PanicLevel { + if entry.Level >= PanicLevel { entry.log(PanicLevel, fmt.Sprint(args...)) } panic(fmt.Sprint(args...)) @@ -153,13 +154,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.Level >= DebugLevel { entry.Debug(fmt.Sprintf(format, args...)) } } func (entry *Entry) Infof(format string, args ...interface{}) { - if entry.Logger.Level >= InfoLevel { + if entry.Level >= InfoLevel { entry.Info(fmt.Sprintf(format, args...)) } } @@ -169,7 +170,7 @@ func (entry *Entry) Printf(format string, args ...interface{}) { } func (entry *Entry) Warnf(format string, args ...interface{}) { - if entry.Logger.Level >= WarnLevel { + if entry.Level >= WarnLevel { entry.Warn(fmt.Sprintf(format, args...)) } } @@ -179,20 +180,20 @@ func (entry *Entry) Warningf(format string, args ...interface{}) { } func (entry *Entry) Errorf(format string, args ...interface{}) { - if entry.Logger.Level >= ErrorLevel { + if entry.Level >= ErrorLevel { entry.Error(fmt.Sprintf(format, args...)) } } func (entry *Entry) Fatalf(format string, args ...interface{}) { - if entry.Logger.Level >= FatalLevel { + if entry.Level >= FatalLevel { entry.Fatal(fmt.Sprintf(format, args...)) } os.Exit(1) } func (entry *Entry) Panicf(format string, args ...interface{}) { - if entry.Logger.Level >= PanicLevel { + if entry.Level >= PanicLevel { entry.Panic(fmt.Sprintf(format, args...)) } } @@ -200,13 +201,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.Level >= DebugLevel { entry.Debug(entry.sprintlnn(args...)) } } func (entry *Entry) Infoln(args ...interface{}) { - if entry.Logger.Level >= InfoLevel { + if entry.Level >= InfoLevel { entry.Info(entry.sprintlnn(args...)) } } @@ -216,7 +217,7 @@ func (entry *Entry) Println(args ...interface{}) { } func (entry *Entry) Warnln(args ...interface{}) { - if entry.Logger.Level >= WarnLevel { + if entry.Level >= WarnLevel { entry.Warn(entry.sprintlnn(args...)) } } @@ -226,20 +227,20 @@ func (entry *Entry) Warningln(args ...interface{}) { } func (entry *Entry) Errorln(args ...interface{}) { - if entry.Logger.Level >= ErrorLevel { + if entry.Level >= ErrorLevel { entry.Error(entry.sprintlnn(args...)) } } func (entry *Entry) Fatalln(args ...interface{}) { - if entry.Logger.Level >= FatalLevel { + if entry.Level >= FatalLevel { entry.Fatal(entry.sprintlnn(args...)) } os.Exit(1) } func (entry *Entry) Panicln(args ...interface{}) { - if entry.Logger.Level >= PanicLevel { + if entry.Level >= PanicLevel { entry.Panic(entry.sprintlnn(args...)) } } diff --git a/entry_test.go b/entry_test.go index 98717df49..f7de400a3 100644 --- a/entry_test.go +++ b/entry_test.go @@ -51,3 +51,17 @@ func TestEntryPanicf(t *testing.T) { entry := NewEntry(logger) entry.WithField("err", errBoom).Panicf("kaboom %v", true) } + +func TestEntryLogLevel(t *testing.T) { + out := &bytes.Buffer{} + logger := New() + logger.Out = out + logger.Level = DebugLevel + entry := NewEntry(logger) + assert.Equal(t, DebugLevel, entry.Level) + entry.Level = WarnLevel + entry.Info("it should not be displayed") + assert.Equal(t, "", out.String()) + entry.Warn("it should be displayed") + assert.Contains(t, out.String(), "it should be displayed") +} From 60e7ca8d24ca3b74c439a90ef6ac10dee1e36433 Mon Sep 17 00:00:00 2001 From: evalphobia Date: Tue, 7 Jul 2015 12:54:35 +0900 Subject: [PATCH 044/547] Include fluentd in list of available hooks --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4be378476..bd9ffb6ed 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,7 @@ func init() { | [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 | #### Level logging From 3720457fbbf9b84a20dff3d0321964458c3956da Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 7 Jul 2015 05:31:24 -0400 Subject: [PATCH 045/547] version: bump to 0.8.3 --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a358b3d3c..cf2f0d1c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ -# (Unreleased) +# 0.8.3 -logrus: fix entry log level (#208) +logrus/core: fix entry log level (#208) logrus/core: improve performance of text formatter by 40% logrus/core: expose `LevelHooks` type logrus/core: add support for DragonflyBSD and NetBSD +formatter/text: print structs more verbosely # 0.8.2 From 0b92d824c45a9c5a96db69c61bba9a8b881669a3 Mon Sep 17 00:00:00 2001 From: Adam Wonak Date: Tue, 7 Jul 2015 11:41:45 -0500 Subject: [PATCH 046/547] Added a new SentryHook initialization func for setting tags. --- hooks/sentry/sentry.go | 15 +++++++++++++-- hooks/sentry/sentry_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/hooks/sentry/sentry.go b/hooks/sentry/sentry.go index e7e45b21b..4d184b2fc 100644 --- a/hooks/sentry/sentry.go +++ b/hooks/sentry/sentry.go @@ -2,8 +2,8 @@ package logrus_sentry import ( "fmt" - "time" "net/http" + "time" "github.com/Sirupsen/logrus" "github.com/getsentry/raven-go" @@ -68,7 +68,18 @@ type SentryHook struct { // and initializes the raven client. // This method sets the timeout to 100 milliseconds. func NewSentryHook(DSN string, levels []logrus.Level) (*SentryHook, error) { - client, err := raven.NewClient(DSN, nil) + client, err := raven.New(DSN) + if err != nil { + return nil, err + } + return &SentryHook{100 * time.Millisecond, client, levels}, nil +} + +// NewWithTagsSentryHook creates a hook with tags to be added to an instance +// of logger and initializes the raven client. This method sets the timeout to +// 100 milliseconds. +func NewWithTagsSentryHook(DSN string, tags map[string]string, levels []logrus.Level) (*SentryHook, error) { + client, err := raven.NewWithTags(DSN, tags) if err != nil { return nil, err } diff --git a/hooks/sentry/sentry_test.go b/hooks/sentry/sentry_test.go index 5f3696b29..5f59f699c 100644 --- a/hooks/sentry/sentry_test.go +++ b/hooks/sentry/sentry_test.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "net/http" "net/http/httptest" + "reflect" "strings" "testing" @@ -98,3 +99,34 @@ func TestSentryHandler(t *testing.T) { } }) } + +func TestSentryTags(t *testing.T) { + WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) { + logger := getTestLogger() + tags := map[string]string{ + "site": "test", + } + levels := []logrus.Level{ + logrus.ErrorLevel, + } + + hook, err := NewWithTagsSentryHook(dsn, tags, levels) + if err != nil { + t.Fatal(err.Error()) + } + + logger.Hooks.Add(hook) + + logger.Error(message) + packet := <-pch + expected := raven.Tags{ + raven.Tag{ + Key: "site", + Value: "test", + }, + } + if !reflect.DeepEqual(packet.Tags, expected) { + t.Errorf("message should have been %s, was %s", message, packet.Message) + } + }) +} From cfc3400127d1021b2854f2dc001a25640e558229 Mon Sep 17 00:00:00 2001 From: Adam Wonak Date: Tue, 7 Jul 2015 11:52:22 -0500 Subject: [PATCH 047/547] updated readme with usage example --- hooks/sentry/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/hooks/sentry/README.md b/hooks/sentry/README.md index 4e1c1476f..8b1f9a16f 100644 --- a/hooks/sentry/README.md +++ b/hooks/sentry/README.md @@ -31,6 +31,22 @@ func main() { } ``` +If you wish to initialize a SentryHook with tags, you can use the `NewWithTagsSentryHook` constructor to provide default tags: + +```go +tags := map[string]string{ + "site": "example.com", +} +levels := []logrus.Level{ + logrus.PanicLevel, + logrus.FatalLevel, + logrus.ErrorLevel, +} +hook, err := logrus_sentry.NewWithTagsSentryHook(YOUR_DSN, tags, levels) + +``` + + ## Special fields Some logrus fields have a special meaning in this hook, From 86213c0870f02130b091207226f3ef0852e81815 Mon Sep 17 00:00:00 2001 From: Allan Glen Date: Thu, 9 Jul 2015 10:05:47 -0600 Subject: [PATCH 048/547] Allow sentry hook to be created using an initialized raven client --- hooks/sentry/README.md | 33 +++++++++++++++++++++++++++++++++ hooks/sentry/sentry.go | 6 ++++++ hooks/sentry/sentry_test.go | 22 ++++++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/hooks/sentry/README.md b/hooks/sentry/README.md index 8b1f9a16f..31de6540a 100644 --- a/hooks/sentry/README.md +++ b/hooks/sentry/README.md @@ -46,6 +46,39 @@ hook, err := logrus_sentry.NewWithTagsSentryHook(YOUR_DSN, tags, levels) ``` +If you wish to initialize a SentryHook with an already initialized raven client, you can use +the `NewWithClientSentryHook` constructor: + +```go +import ( + "github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus/hooks/sentry" + "github.com/getsentry/raven-go" +) + +func main() { + log := logrus.New() + + client, err := raven.New(YOUR_DSN) + if err != nil { + log.Fatal(err) + } + + hook, err := logrus_sentry.NewWithClientSentryHook(client, []logrus.Level{ + logrus.PanicLevel, + logrus.FatalLevel, + logrus.ErrorLevel, + }) + + if err == nil { + log.Hooks.Add(hook) + } +} + +hook, err := NewWithClientSentryHook(client, []logrus.Level{ + logrus.ErrorLevel, +}) +``` ## Special fields diff --git a/hooks/sentry/sentry.go b/hooks/sentry/sentry.go index 4d184b2fc..cf88098a8 100644 --- a/hooks/sentry/sentry.go +++ b/hooks/sentry/sentry.go @@ -86,6 +86,12 @@ func NewWithTagsSentryHook(DSN string, tags map[string]string, levels []logrus.L return &SentryHook{100 * time.Millisecond, client, levels}, nil } +// NewWithClientSentryHook creates a hook using an initialized raven client. +// This method sets the timeout to 100 milliseconds. +func NewWithClientSentryHook(client *raven.Client, levels []logrus.Level) (*SentryHook, error) { + return &SentryHook{100 * time.Millisecond, client, levels}, nil +} + // Called when an event should be sent to sentry // Special fields that sentry uses to give more information to the server // are extracted from entry.Data (if they are found) diff --git a/hooks/sentry/sentry_test.go b/hooks/sentry/sentry_test.go index 5f59f699c..4a97bc63e 100644 --- a/hooks/sentry/sentry_test.go +++ b/hooks/sentry/sentry_test.go @@ -100,6 +100,28 @@ func TestSentryHandler(t *testing.T) { }) } +func TestSentryWithClient(t *testing.T) { + WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) { + logger := getTestLogger() + + client, _ := raven.New(dsn) + + hook, err := NewWithClientSentryHook(client, []logrus.Level{ + logrus.ErrorLevel, + }) + if err != nil { + t.Fatal(err.Error()) + } + logger.Hooks.Add(hook) + + logger.Error(message) + packet := <-pch + if packet.Message != message { + t.Errorf("message should have been %s, was %s", message, packet.Message) + } + }) +} + func TestSentryTags(t *testing.T) { WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) { logger := getTestLogger() From 89a478d41e779a12c6a0d4c3449e816ec7a56924 Mon Sep 17 00:00:00 2001 From: Peng Zhai Date: Thu, 16 Jul 2015 21:02:46 -0400 Subject: [PATCH 049/547] Fix data race issue in TextFormatter. Fix for https://github.com/Sirupsen/logrus/issues/217. --- text_formatter.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index e25f86cdd..5f9ab463a 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -73,14 +73,15 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { isColorTerminal := isTerminal && (runtime.GOOS != "windows") isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors - if f.TimestampFormat == "" { - f.TimestampFormat = DefaultTimestampFormat + timestampFormat := f.TimestampFormat + if timestampFormat == "" { + timestampFormat = DefaultTimestampFormat } if isColored { f.printColored(b, entry, keys) } else { if !f.DisableTimestamp { - f.appendKeyValue(b, "time", entry.Time.Format(f.TimestampFormat)) + f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat)) } f.appendKeyValue(b, "level", entry.Level.String()) f.appendKeyValue(b, "msg", entry.Message) From 99db258ab8d0ded13f80e80ec0686d0acd7d7c93 Mon Sep 17 00:00:00 2001 From: Peng Zhai Date: Mon, 20 Jul 2015 10:43:12 -0700 Subject: [PATCH 050/547] Change printColored to use timestampFormat. --- text_formatter.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index 5f9ab463a..17cc29848 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -78,7 +78,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { timestampFormat = DefaultTimestampFormat } if isColored { - f.printColored(b, entry, keys) + f.printColored(b, entry, keys, timestampFormat) } else { if !f.DisableTimestamp { f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat)) @@ -94,7 +94,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { return b.Bytes(), nil } -func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string) { +func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) { var levelColor int switch entry.Level { case DebugLevel: @@ -112,7 +112,7 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin if !f.FullTimestamp { fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) } else { - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(f.TimestampFormat), entry.Message) + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message) } for _, k := range keys { v := entry.Data[k] From 62bb41be614a045e0a762c7cd96c769f8d88295f Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Wed, 22 Jul 2015 11:29:20 -0400 Subject: [PATCH 051/547] changelog: add 218 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf2f0d1c6..7e5fe2f20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.8.4 + +formatter/text: fix data race (#218) + # 0.8.3 logrus/core: fix entry log level (#208) From 6c8a4b2ea511f48af082c5f044a0435afd329620 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 23 Jul 2015 21:45:57 -0400 Subject: [PATCH 052/547] Revert "Merge pull request #208 from Arkan/master" This reverts commit f46e803f14fe52d490f2ac46ca15c6717e812393, reversing changes made to 081376046542a22336972a1b51b2ee3105acdc06. --- CHANGELOG.md | 4 ++++ entry.go | 41 ++++++++++++++++++++--------------------- entry_test.go | 14 -------------- 3 files changed, 24 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e5fe2f20..e696bc197 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.8.5 + +logrus/core: revert #208 + # 0.8.4 formatter/text: fix data race (#218) diff --git a/entry.go b/entry.go index 2a980651e..699ea035c 100644 --- a/entry.go +++ b/entry.go @@ -32,8 +32,7 @@ func NewEntry(logger *Logger) *Entry { return &Entry{ Logger: logger, // Default is three fields, give a little extra room - Data: make(Fields, 5), - Level: logger.Level, + Data: make(Fields, 5), } } @@ -68,7 +67,7 @@ func (entry *Entry) WithFields(fields Fields) *Entry { for k, v := range fields { data[k] = v } - return &Entry{Logger: entry.Logger, Data: data, Level: entry.Level} + return &Entry{Logger: entry.Logger, Data: data} } func (entry *Entry) log(level Level, msg string) { @@ -106,7 +105,7 @@ func (entry *Entry) log(level Level, msg string) { } func (entry *Entry) Debug(args ...interface{}) { - if entry.Level >= DebugLevel { + if entry.Logger.Level >= DebugLevel { entry.log(DebugLevel, fmt.Sprint(args...)) } } @@ -116,13 +115,13 @@ func (entry *Entry) Print(args ...interface{}) { } func (entry *Entry) Info(args ...interface{}) { - if entry.Level >= InfoLevel { + if entry.Logger.Level >= InfoLevel { entry.log(InfoLevel, fmt.Sprint(args...)) } } func (entry *Entry) Warn(args ...interface{}) { - if entry.Level >= WarnLevel { + if entry.Logger.Level >= WarnLevel { entry.log(WarnLevel, fmt.Sprint(args...)) } } @@ -132,20 +131,20 @@ func (entry *Entry) Warning(args ...interface{}) { } func (entry *Entry) Error(args ...interface{}) { - if entry.Level >= ErrorLevel { + if entry.Logger.Level >= ErrorLevel { entry.log(ErrorLevel, fmt.Sprint(args...)) } } func (entry *Entry) Fatal(args ...interface{}) { - if entry.Level >= FatalLevel { + if entry.Logger.Level >= FatalLevel { entry.log(FatalLevel, fmt.Sprint(args...)) } os.Exit(1) } func (entry *Entry) Panic(args ...interface{}) { - if entry.Level >= PanicLevel { + if entry.Logger.Level >= PanicLevel { entry.log(PanicLevel, fmt.Sprint(args...)) } panic(fmt.Sprint(args...)) @@ -154,13 +153,13 @@ func (entry *Entry) Panic(args ...interface{}) { // Entry Printf family functions func (entry *Entry) Debugf(format string, args ...interface{}) { - if entry.Level >= DebugLevel { + if entry.Logger.Level >= DebugLevel { entry.Debug(fmt.Sprintf(format, args...)) } } func (entry *Entry) Infof(format string, args ...interface{}) { - if entry.Level >= InfoLevel { + if entry.Logger.Level >= InfoLevel { entry.Info(fmt.Sprintf(format, args...)) } } @@ -170,7 +169,7 @@ func (entry *Entry) Printf(format string, args ...interface{}) { } func (entry *Entry) Warnf(format string, args ...interface{}) { - if entry.Level >= WarnLevel { + if entry.Logger.Level >= WarnLevel { entry.Warn(fmt.Sprintf(format, args...)) } } @@ -180,20 +179,20 @@ func (entry *Entry) Warningf(format string, args ...interface{}) { } func (entry *Entry) Errorf(format string, args ...interface{}) { - if entry.Level >= ErrorLevel { + if entry.Logger.Level >= ErrorLevel { entry.Error(fmt.Sprintf(format, args...)) } } func (entry *Entry) Fatalf(format string, args ...interface{}) { - if entry.Level >= FatalLevel { + if entry.Logger.Level >= FatalLevel { entry.Fatal(fmt.Sprintf(format, args...)) } os.Exit(1) } func (entry *Entry) Panicf(format string, args ...interface{}) { - if entry.Level >= PanicLevel { + if entry.Logger.Level >= PanicLevel { entry.Panic(fmt.Sprintf(format, args...)) } } @@ -201,13 +200,13 @@ func (entry *Entry) Panicf(format string, args ...interface{}) { // Entry Println family functions func (entry *Entry) Debugln(args ...interface{}) { - if entry.Level >= DebugLevel { + if entry.Logger.Level >= DebugLevel { entry.Debug(entry.sprintlnn(args...)) } } func (entry *Entry) Infoln(args ...interface{}) { - if entry.Level >= InfoLevel { + if entry.Logger.Level >= InfoLevel { entry.Info(entry.sprintlnn(args...)) } } @@ -217,7 +216,7 @@ func (entry *Entry) Println(args ...interface{}) { } func (entry *Entry) Warnln(args ...interface{}) { - if entry.Level >= WarnLevel { + if entry.Logger.Level >= WarnLevel { entry.Warn(entry.sprintlnn(args...)) } } @@ -227,20 +226,20 @@ func (entry *Entry) Warningln(args ...interface{}) { } func (entry *Entry) Errorln(args ...interface{}) { - if entry.Level >= ErrorLevel { + if entry.Logger.Level >= ErrorLevel { entry.Error(entry.sprintlnn(args...)) } } func (entry *Entry) Fatalln(args ...interface{}) { - if entry.Level >= FatalLevel { + if entry.Logger.Level >= FatalLevel { entry.Fatal(entry.sprintlnn(args...)) } os.Exit(1) } func (entry *Entry) Panicln(args ...interface{}) { - if entry.Level >= PanicLevel { + if entry.Logger.Level >= PanicLevel { entry.Panic(entry.sprintlnn(args...)) } } diff --git a/entry_test.go b/entry_test.go index f7de400a3..98717df49 100644 --- a/entry_test.go +++ b/entry_test.go @@ -51,17 +51,3 @@ func TestEntryPanicf(t *testing.T) { entry := NewEntry(logger) entry.WithField("err", errBoom).Panicf("kaboom %v", true) } - -func TestEntryLogLevel(t *testing.T) { - out := &bytes.Buffer{} - logger := New() - logger.Out = out - logger.Level = DebugLevel - entry := NewEntry(logger) - assert.Equal(t, DebugLevel, entry.Level) - entry.Level = WarnLevel - entry.Info("it should not be displayed") - assert.Equal(t, "", out.String()) - entry.Warn("it should be displayed") - assert.Contains(t, out.String(), "it should be displayed") -} From 4ea3ffcad63397b04b86da4a83720219d75456a0 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 23 Jul 2015 21:49:03 -0400 Subject: [PATCH 053/547] changelog: reformat for proper markdown --- CHANGELOG.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e696bc197..ab1844010 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,37 +1,37 @@ # 0.8.5 -logrus/core: revert #208 +* logrus/core: revert #208 # 0.8.4 -formatter/text: fix data race (#218) +* formatter/text: fix data race (#218) # 0.8.3 -logrus/core: fix entry log level (#208) -logrus/core: improve performance of text formatter by 40% -logrus/core: expose `LevelHooks` type -logrus/core: add support for DragonflyBSD and NetBSD -formatter/text: print structs more verbosely +* logrus/core: fix entry log level (#208) +* logrus/core: improve performance of text formatter by 40% +* logrus/core: expose `LevelHooks` type +* logrus/core: add support for DragonflyBSD and NetBSD +* formatter/text: print structs more verbosely # 0.8.2 -logrus: fix more Fatal family functions +* logrus: fix more Fatal family functions # 0.8.1 -logrus: fix not exiting on `Fatalf` and `Fatalln` +* logrus: fix not exiting on `Fatalf` and `Fatalln` # 0.8.0 -logrus: defaults to stderr instead of stdout -hooks/sentry: add special field for `*http.Request` -formatter/text: ignore Windows for colors +* logrus: defaults to stderr instead of stdout +* hooks/sentry: add special field for `*http.Request` +* formatter/text: ignore Windows for colors # 0.7.3 -formatter/\*: allow configuration of timestamp layout +* formatter/\*: allow configuration of timestamp layout # 0.7.2 -formatter/text: Add configuration option for time format (#158) +* formatter/text: Add configuration option for time format (#158) From 74ff271e0c92ee7693e76e9543cf15cf41c17bfc Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 28 Jul 2015 08:10:54 -0400 Subject: [PATCH 054/547] changelog: update with raven change --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab1844010..b1fe4b62c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.8.6 + +* hooks/raven: allow passing an initialized client + # 0.8.5 * logrus/core: revert #208 From d276e1ca792717d7b9e7571c5414393e24c55166 Mon Sep 17 00:00:00 2001 From: Marcos Lilljedahl Date: Thu, 6 Aug 2015 21:00:57 -0300 Subject: [PATCH 055/547] Make log method receive a copy of Entry structure to avoid race conditions Fixes #216 Signed-off-by: Marcos Lilljedahl --- entry.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/entry.go b/entry.go index 699ea035c..8a522f047 100644 --- a/entry.go +++ b/entry.go @@ -70,12 +70,12 @@ func (entry *Entry) WithFields(fields Fields) *Entry { return &Entry{Logger: entry.Logger, Data: data} } -func (entry *Entry) log(level Level, msg string) { +func (entry Entry) log(level Level, msg string) { entry.Time = time.Now() entry.Level = level entry.Message = msg - if err := entry.Logger.Hooks.Fire(level, entry); err != nil { + if err := entry.Logger.Hooks.Fire(level, &entry); err != nil { entry.Logger.mu.Lock() fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) entry.Logger.mu.Unlock() From 4d955ac8745a51bf8cb4624a0f30108ed796f975 Mon Sep 17 00:00:00 2001 From: Marcos Lilljedahl Date: Thu, 6 Aug 2015 21:19:22 -0300 Subject: [PATCH 056/547] Fix panic return type Signed-off-by: Marcos Lilljedahl --- entry.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entry.go b/entry.go index 8a522f047..001c4c779 100644 --- a/entry.go +++ b/entry.go @@ -100,7 +100,7 @@ func (entry Entry) log(level Level, msg string) { // panic() to use in Entry#Panic(), we avoid the allocation by checking // directly here. if level <= PanicLevel { - panic(entry) + panic(&entry) } } From d2f0f265f9f53b265adc9cf8c6c93f948c71570e Mon Sep 17 00:00:00 2001 From: Viet Hung Nguyen Date: Fri, 7 Aug 2015 15:56:08 +0700 Subject: [PATCH 057/547] Update logger.go --- logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logger.go b/logger.go index e4974bfbe..dd9975931 100644 --- a/logger.go +++ b/logger.go @@ -8,7 +8,7 @@ import ( type Logger struct { // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a - // file, or leave it default which is `os.Stdout`. You can also set this to + // file, or leave it default which is `os.Stderr`. You can also set this to // something more adventorous, such as logging to Kafka. Out io.Writer // Hooks for the logger instance. These allow firing events based on logging From 81667e14d0b9018f4a1e97024153c0d2467c3a8c Mon Sep 17 00:00:00 2001 From: Marcos Lilljedahl Date: Fri, 7 Aug 2015 18:29:20 -0300 Subject: [PATCH 058/547] Add comment to log function Signed-off-by: Marcos Lilljedahl --- entry.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/entry.go b/entry.go index 001c4c779..04673a075 100644 --- a/entry.go +++ b/entry.go @@ -70,6 +70,8 @@ func (entry *Entry) WithFields(fields Fields) *Entry { return &Entry{Logger: entry.Logger, Data: data} } +// This function is not declared with a pointer value because otherwise +// race conditions will occur when using multiple goroutines func (entry Entry) log(level Level, msg string) { entry.Time = time.Now() entry.Level = level From 3e254625b731fb433fe52f5e00f4de14db7e0110 Mon Sep 17 00:00:00 2001 From: Diego Bernardes de Sousa Pinto Date: Tue, 18 Aug 2015 15:15:02 -0300 Subject: [PATCH 059/547] Update README.md Just a small fix. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd9ffb6ed..17837c933 100644 --- a/README.md +++ b/README.md @@ -273,7 +273,7 @@ init() { // do something here to set environment depending on an environment variable // or command-line flag if Environment == "production" { - log.SetFormatter(&logrus.JSONFormatter{}) + log.SetFormatter(&log.JSONFormatter{}) } else { // The TextFormatter is default, you don't actually have to do this. log.SetFormatter(&log.TextFormatter{}) From d411e225198e20a7c91f06824a56c8656f17ae39 Mon Sep 17 00:00:00 2001 From: Ryan Faerman Date: Thu, 20 Aug 2015 00:07:28 -0400 Subject: [PATCH 060/547] Fixes typo in WithField documentation --- logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logger.go b/logger.go index dd9975931..fd9804c64 100644 --- a/logger.go +++ b/logger.go @@ -53,7 +53,7 @@ func New() *Logger { // Adds a field to the log entry, note that you it doesn't log until you call // Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry. -// Ff you want multiple fields, use `WithFields`. +// If you want multiple fields, use `WithFields`. func (logger *Logger) WithField(key string, value interface{}) *Entry { return NewEntry(logger).WithField(key, value) } From e2c06c692de70a5c5edd21204d047616de6a3441 Mon Sep 17 00:00:00 2001 From: weekface Date: Thu, 27 Aug 2015 09:54:11 +0800 Subject: [PATCH 061/547] Add hook for logging to mongodb --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 17837c933..9f712a7e7 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,7 @@ func init() { | [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 | #### Level logging From 6b2977e44bb8defa6ae4f4f3854ad38efd7f4212 Mon Sep 17 00:00:00 2001 From: Linus Gasser Date: Fri, 4 Sep 2015 12:02:14 +0200 Subject: [PATCH 062/547] Update README.md Typo it seems - except if I still didn't understand go... --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9f712a7e7..6fa6e2062 100644 --- a/README.md +++ b/README.md @@ -317,7 +317,7 @@ type MyJSONFormatter struct { log.SetFormatter(new(MyJSONFormatter)) -func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { +func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) { // Note this doesn't include Time, Level and Message which are available on // the Entry. Consult `godoc` on information about those fields or read the // source of the official loggers. From f740b3557f62a650638458bc7e9a7b7fd2cacf93 Mon Sep 17 00:00:00 2001 From: Mawuli Adzoe Date: Mon, 7 Sep 2015 09:10:15 +0000 Subject: [PATCH 063/547] Add godoc summary --- doc.go | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 doc.go diff --git a/doc.go b/doc.go new file mode 100644 index 000000000..445dc5592 --- /dev/null +++ b/doc.go @@ -0,0 +1,6 @@ +/* +Package logrus is a structured logger for Go, completely API compatible with the standard library logger. + +For a full guide visit https://github.com/Sirupsen/logrus +*/ +package logrus From 52ea9af00baf844a466948528fff0e3a11514c72 Mon Sep 17 00:00:00 2001 From: Mawuli Adzoe Date: Mon, 7 Sep 2015 09:10:36 +0000 Subject: [PATCH 064/547] Add sample code --- doc.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/doc.go b/doc.go index 445dc5592..dddd5f877 100644 --- a/doc.go +++ b/doc.go @@ -1,6 +1,26 @@ /* Package logrus is a structured logger for Go, completely API compatible with the standard library logger. + +The simplest way to use Logrus is simply the package-level exported logger: + + package main + + import ( + log "github.com/Sirupsen/logrus" + ) + + func main() { + log.WithFields(log.Fields{ + "animal": "walrus", + "number": 1, + "size": 10, + }).Info("A walrus appears") + } + +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 */ package logrus From 6bd93cdae2ebf30362392036acea7b36559a4a9b Mon Sep 17 00:00:00 2001 From: Antoine Grondin Date: Mon, 7 Sep 2015 20:47:46 -0400 Subject: [PATCH 065/547] Revert "Implement casting of *Entry to error." This reverts commit dd3f39332d1aeaa3432648cf9790673812cb2828. --- entry.go | 68 ++++++++++++++++++--------------------------------- entry_test.go | 19 ++------------ 2 files changed, 26 insertions(+), 61 deletions(-) diff --git a/entry.go b/entry.go index 1e97f99dd..9ae900bc5 100644 --- a/entry.go +++ b/entry.go @@ -56,14 +56,6 @@ func (entry *Entry) String() (string, error) { return reader.String(), err } -// ToError returns the field value of ErrorKey (nil) -func (entry *Entry) ToError() error { - if err, ok := entry.Data[ErrorKey].(error); ok { - return err - } - return nil -} - // Add an error as single field (using the key defined in ErrorKey) to the Entry. func (entry *Entry) WithError(err error) *Entry { return entry.WithField(ErrorKey, err) @@ -122,40 +114,36 @@ func (entry Entry) log(level Level, msg string) { } } -func (entry *Entry) Debug(args ...interface{}) *Entry { +func (entry *Entry) Debug(args ...interface{}) { if entry.Logger.Level >= DebugLevel { entry.log(DebugLevel, fmt.Sprint(args...)) } - return entry } -func (entry *Entry) Print(args ...interface{}) *Entry { - return entry.Info(args...) +func (entry *Entry) Print(args ...interface{}) { + entry.Info(args...) } -func (entry *Entry) Info(args ...interface{}) *Entry { +func (entry *Entry) Info(args ...interface{}) { if entry.Logger.Level >= InfoLevel { entry.log(InfoLevel, fmt.Sprint(args...)) } - return entry } -func (entry *Entry) Warn(args ...interface{}) *Entry { +func (entry *Entry) Warn(args ...interface{}) { if entry.Logger.Level >= WarnLevel { entry.log(WarnLevel, fmt.Sprint(args...)) } - return entry } -func (entry *Entry) Warning(args ...interface{}) *Entry { - return entry.Warn(args...) +func (entry *Entry) Warning(args ...interface{}) { + entry.Warn(args...) } -func (entry *Entry) Error(args ...interface{}) *Entry { +func (entry *Entry) Error(args ...interface{}) { if entry.Logger.Level >= ErrorLevel { entry.log(ErrorLevel, fmt.Sprint(args...)) } - return entry } func (entry *Entry) Fatal(args ...interface{}) { @@ -174,40 +162,36 @@ func (entry *Entry) Panic(args ...interface{}) { // Entry Printf family functions -func (entry *Entry) Debugf(format string, args ...interface{}) *Entry { +func (entry *Entry) Debugf(format string, args ...interface{}) { if entry.Logger.Level >= DebugLevel { entry.Debug(fmt.Sprintf(format, args...)) } - return entry } -func (entry *Entry) Infof(format string, args ...interface{}) *Entry { +func (entry *Entry) Infof(format string, args ...interface{}) { if entry.Logger.Level >= InfoLevel { entry.Info(fmt.Sprintf(format, args...)) } - return entry } -func (entry *Entry) Printf(format string, args ...interface{}) *Entry { - return entry.Infof(format, args...) +func (entry *Entry) Printf(format string, args ...interface{}) { + entry.Infof(format, args...) } -func (entry *Entry) Warnf(format string, args ...interface{}) *Entry { +func (entry *Entry) Warnf(format string, args ...interface{}) { if entry.Logger.Level >= WarnLevel { entry.Warn(fmt.Sprintf(format, args...)) } - return entry } -func (entry *Entry) Warningf(format string, args ...interface{}) *Entry { - return entry.Warnf(format, args...) +func (entry *Entry) Warningf(format string, args ...interface{}) { + entry.Warnf(format, args...) } -func (entry *Entry) Errorf(format string, args ...interface{}) *Entry { +func (entry *Entry) Errorf(format string, args ...interface{}) { if entry.Logger.Level >= ErrorLevel { entry.Error(fmt.Sprintf(format, args...)) } - return entry } func (entry *Entry) Fatalf(format string, args ...interface{}) { @@ -225,40 +209,36 @@ func (entry *Entry) Panicf(format string, args ...interface{}) { // Entry Println family functions -func (entry *Entry) Debugln(args ...interface{}) *Entry { +func (entry *Entry) Debugln(args ...interface{}) { if entry.Logger.Level >= DebugLevel { entry.Debug(entry.sprintlnn(args...)) } - return entry } -func (entry *Entry) Infoln(args ...interface{}) *Entry { +func (entry *Entry) Infoln(args ...interface{}) { if entry.Logger.Level >= InfoLevel { entry.Info(entry.sprintlnn(args...)) } - return entry } -func (entry *Entry) Println(args ...interface{}) *Entry { - return entry.Infoln(args...) +func (entry *Entry) Println(args ...interface{}) { + entry.Infoln(args...) } -func (entry *Entry) Warnln(args ...interface{}) *Entry { +func (entry *Entry) Warnln(args ...interface{}) { if entry.Logger.Level >= WarnLevel { entry.Warn(entry.sprintlnn(args...)) } - return entry } -func (entry *Entry) Warningln(args ...interface{}) *Entry { - return entry.Warnln(args...) +func (entry *Entry) Warningln(args ...interface{}) { + entry.Warnln(args...) } -func (entry *Entry) Errorln(args ...interface{}) *Entry { +func (entry *Entry) Errorln(args ...interface{}) { if entry.Logger.Level >= ErrorLevel { entry.Error(entry.sprintlnn(args...)) } - return entry } func (entry *Entry) Fatalln(args ...interface{}) { diff --git a/entry_test.go b/entry_test.go index 3473ffb66..99c3b41d5 100644 --- a/entry_test.go +++ b/entry_test.go @@ -8,23 +8,6 @@ import ( "github.com/stretchr/testify/assert" ) -var err = fmt.Errorf("kaboom at layer %d", 4711) - -func TestToError(t *testing.T) { - - assert := assert.New(t) - - ctx := WithField("foo", "bar") - assert.Equal(nil, ctx.Debug("Hello").ToError()) - - ctx.Data[ErrorKey] = "error" - assert.Equal(nil, ctx.Debug("Hello").ToError()) - - ctx = ctx.WithError(err) - assert.Equal(err, ctx.Debug("Hello").ToError()) - -} - func TestEntryWithError(t *testing.T) { assert := assert.New(t) @@ -33,6 +16,8 @@ func TestEntryWithError(t *testing.T) { ErrorKey = "error" }() + err := fmt.Errorf("kaboom at layer %d", 4711) + assert.Equal(err, WithError(err).Data["error"]) logger := New() From e2af3d0d8a5ffc71ec11d7917f5dc05217e82f3b Mon Sep 17 00:00:00 2001 From: Antoine Grondin Date: Mon, 7 Sep 2015 20:49:55 -0400 Subject: [PATCH 066/547] add compile checks for StdLogger interface --- logrus.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/logrus.go b/logrus.go index 43ee12e90..0c09fbc26 100644 --- a/logrus.go +++ b/logrus.go @@ -74,7 +74,11 @@ const ( ) // Won't compile if StdLogger can't be realized by a log.Logger -var _ StdLogger = &log.Logger{} +var ( + _ StdLogger = &log.Logger{} + _ StdLogger = &Entry{} + _ StdLogger = &Logger{} +) // StdLogger is what your logrus-enabled library should take, that way // it'll accept a stdlib logger and a logrus logger. There's no standard From d094539baca226ea54c6e0ed80f7b83902d525ed Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 8 Sep 2015 20:46:18 +0000 Subject: [PATCH 067/547] v0.8.7 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1fe4b62c..78f98959b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 0.8.7 + +* logrus/core: fix possible race (#216) +* logrus/doc: small typo fixes and doc improvements + + # 0.8.6 * hooks/raven: allow passing an initialized client From 5de0a42fe4acc135fe671371b7316cf2c4a855e5 Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Fri, 19 Jun 2015 17:20:59 -0700 Subject: [PATCH 068/547] Don't emit a msg if there is none to emit --- text_formatter.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text_formatter.go b/text_formatter.go index 17cc29848..06ef20233 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -84,7 +84,9 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat)) } f.appendKeyValue(b, "level", entry.Level.String()) - f.appendKeyValue(b, "msg", entry.Message) + if entry.Message != "" { + f.appendKeyValue(b, "msg", entry.Message) + } for _, key := range keys { f.appendKeyValue(b, key, entry.Data[key]) } From bdbb80b4b0d08a61cb316c311d66dd8187d2b68b Mon Sep 17 00:00:00 2001 From: devopstaku Date: Wed, 9 Sep 2015 21:55:50 +0800 Subject: [PATCH 069/547] Add description about how to connect to local syslog with syslog hook --- README.md | 2 +- hooks/syslog/README.md | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6fa6e2062..2a9d3681d 100644 --- a/README.md +++ b/README.md @@ -198,7 +198,7 @@ func init() { } } ``` - +Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md). | Hook | Description | | ----- | ----------- | diff --git a/hooks/syslog/README.md b/hooks/syslog/README.md index 4dbb8e729..066704b37 100644 --- a/hooks/syslog/README.md +++ b/hooks/syslog/README.md @@ -18,3 +18,22 @@ func main() { } } ``` + +If you want to connect to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). Just assign empty string to the first two parameters of `NewSyslogHook`. It should look like the following. + +```go +import ( + "log/syslog" + "github.com/Sirupsen/logrus" + logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" +) + +func main() { + log := logrus.New() + hook, err := logrus_syslog.NewSyslogHook("", "", syslog.LOG_INFO, "") + + if err == nil { + log.Hooks.Add(hook) + } +} +``` \ No newline at end of file From ebc56a049f7e2199e772b49fa2d16f8c235f98dc Mon Sep 17 00:00:00 2001 From: Maksim Naumov Date: Sat, 12 Sep 2015 22:41:06 +0200 Subject: [PATCH 070/547] `TextMarshaler` and `TextUnmarshaler` implementation for `Level` --- logrus.go | 18 ++++++++++++++++++ logrus_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/logrus.go b/logrus.go index 0c09fbc26..ca7ad77c0 100644 --- a/logrus.go +++ b/logrus.go @@ -31,6 +31,24 @@ func (level Level) String() string { return "unknown" } +// UnmarshalText decodes text to the level. +func (level *Level) UnmarshalText(text []byte) error { + if len(text) == 0 { + return nil + } + parsed, err := ParseLevel(string(text)) + if err != nil { + return err + } + *level = parsed + return nil +} + +// MarshalText encodes the level into UTF-8-encoded text and returns the result. +func (level Level) MarshalText() (text []byte, err error) { + return []byte(level.String()), nil +} + // ParseLevel takes a string level and returns the Logrus log level constant. func ParseLevel(lvl string) (Level, error) { switch lvl { diff --git a/logrus_test.go b/logrus_test.go index efaacea23..faa79f6d4 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -283,6 +283,46 @@ func TestParseLevel(t *testing.T) { assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error()) } +func TestUnmarshalLogLevel(t *testing.T) { + jsonString := `["debug","info","warning","error","fatal","panic"]` + var levels []Level + + err := json.Unmarshal([]byte(jsonString), &levels) + t.Logf("%v", levels) + + assert.Nil(t, err) + assert.Equal(t, 6, len(levels)) + assert.Equal(t, DebugLevel, levels[0]) + assert.Equal(t, InfoLevel, levels[1]) + assert.Equal(t, WarnLevel, levels[2]) + assert.Equal(t, ErrorLevel, levels[3]) + assert.Equal(t, FatalLevel, levels[4]) + assert.Equal(t, PanicLevel, levels[5]) +} + +func TestMarshalLogLevel(t *testing.T) { + str := struct { + Debug Level + Info Level + Warn Level + Error Level + Fatal Level + Panic Level + }{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, FatalLevel, PanicLevel} + + text, err := json.Marshal(str) + testStr := string(text) + t.Logf("%s", testStr) + + assert.Nil(t, err) + assert.Contains(t, testStr, `"Debug":"debug"`) + assert.Contains(t, testStr, `"Info":"info"`) + assert.Contains(t, testStr, `"Warn":"warning"`) + assert.Contains(t, testStr, `"Error":"error"`) + assert.Contains(t, testStr, `"Fatal":"fatal"`) + assert.Contains(t, testStr, `"Panic":"panic"`) +} + func TestGetSetLevelRace(t *testing.T) { wg := sync.WaitGroup{} for i := 0; i < 100; i++ { From 42207e0f627c8d2effe3905f680d89936d25dc0a Mon Sep 17 00:00:00 2001 From: Lorenz Leutgeb Date: Thu, 17 Sep 2015 19:37:51 +0200 Subject: [PATCH 071/547] Improve references to Logstash formatter --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6fa6e2062..1336e07ff 100644 --- a/README.md +++ b/README.md @@ -296,10 +296,10 @@ The built-in logging formatters are: field to `true`. To force no colored output even if there is a TTY set the `DisableColors` field to `true` * `logrus.JSONFormatter`. Logs fields as JSON. -* `logrus_logstash.LogstashFormatter`. Logs fields as Logstash Events (http://logstash.net). +* `logrus/formatters/logstash.LogstashFormatter`. Logs fields as [Logstash](http://logstash.net) Events. ```go - logrus.SetFormatter(&logrus_logstash.LogstashFormatter{Type: “application_name"}) + logrus.SetFormatter(&logstash.LogstashFormatter{Type: “application_name"}) ``` Third party logging formatters: From 4574ac76976e81d9a102dfd677713862fdc76822 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 5 Oct 2015 07:45:58 -0400 Subject: [PATCH 072/547] Revert "TextMarshaler and TextUnmarshaler implementation for Level" --- logrus.go | 18 ------------------ logrus_test.go | 40 ---------------------------------------- 2 files changed, 58 deletions(-) diff --git a/logrus.go b/logrus.go index ca7ad77c0..0c09fbc26 100644 --- a/logrus.go +++ b/logrus.go @@ -31,24 +31,6 @@ func (level Level) String() string { return "unknown" } -// UnmarshalText decodes text to the level. -func (level *Level) UnmarshalText(text []byte) error { - if len(text) == 0 { - return nil - } - parsed, err := ParseLevel(string(text)) - if err != nil { - return err - } - *level = parsed - return nil -} - -// MarshalText encodes the level into UTF-8-encoded text and returns the result. -func (level Level) MarshalText() (text []byte, err error) { - return []byte(level.String()), nil -} - // ParseLevel takes a string level and returns the Logrus log level constant. func ParseLevel(lvl string) (Level, error) { switch lvl { diff --git a/logrus_test.go b/logrus_test.go index faa79f6d4..efaacea23 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -283,46 +283,6 @@ func TestParseLevel(t *testing.T) { assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error()) } -func TestUnmarshalLogLevel(t *testing.T) { - jsonString := `["debug","info","warning","error","fatal","panic"]` - var levels []Level - - err := json.Unmarshal([]byte(jsonString), &levels) - t.Logf("%v", levels) - - assert.Nil(t, err) - assert.Equal(t, 6, len(levels)) - assert.Equal(t, DebugLevel, levels[0]) - assert.Equal(t, InfoLevel, levels[1]) - assert.Equal(t, WarnLevel, levels[2]) - assert.Equal(t, ErrorLevel, levels[3]) - assert.Equal(t, FatalLevel, levels[4]) - assert.Equal(t, PanicLevel, levels[5]) -} - -func TestMarshalLogLevel(t *testing.T) { - str := struct { - Debug Level - Info Level - Warn Level - Error Level - Fatal Level - Panic Level - }{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, FatalLevel, PanicLevel} - - text, err := json.Marshal(str) - testStr := string(text) - t.Logf("%s", testStr) - - assert.Nil(t, err) - assert.Contains(t, testStr, `"Debug":"debug"`) - assert.Contains(t, testStr, `"Info":"info"`) - assert.Contains(t, testStr, `"Warn":"warning"`) - assert.Contains(t, testStr, `"Error":"error"`) - assert.Contains(t, testStr, `"Fatal":"fatal"`) - assert.Contains(t, testStr, `"Panic":"panic"`) -} - func TestGetSetLevelRace(t *testing.T) { wg := sync.WaitGroup{} for i := 0; i < 100; i++ { From c0022c1a60fadd438a2ad252f939213ee1bacfea Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 5 Oct 2015 12:05:14 +0000 Subject: [PATCH 073/547] changelog: add for 0.9.0 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78f98959b..d57f6bcd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.9.0 (Unreleased) + +* logrus/text_formatter: don't emit empty msg + # 0.8.7 * logrus/core: fix possible race (#216) From 16e987b1e222518289294348b67df866e79a0ea9 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 5 Oct 2015 12:05:33 +0000 Subject: [PATCH 074/547] travis: remove support for 1.2 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2d8c08661..ec6411425 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: go go: - - 1.2 - 1.3 - 1.4 - tip From e5b0ea3c4be7f3aba10d24208a68907aaa7684d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Lafoucrie=CC=80re?= Date: Tue, 6 Oct 2015 14:28:31 -0400 Subject: [PATCH 075/547] Move airbrake to dedicated repo(s) The current implementation is using an old implementation of the Airbrake v2 api. Airbrake has release v2 since, but this package is necessary for in-house solutions like errbit. The number of errbit users don't justify to continue pushing this version as the official. That's why the official hook is now using gobrake (the official package), and the one coming from logrus is now named "legacy". --- README.md | 16 ++-- hooks/airbrake/airbrake.go | 54 ------------- hooks/airbrake/airbrake_test.go | 133 -------------------------------- 3 files changed, 8 insertions(+), 195 deletions(-) delete mode 100644 hooks/airbrake/airbrake.go delete mode 100644 hooks/airbrake/airbrake_test.go diff --git a/README.md b/README.md index a0c0644e2..b2b676a53 100644 --- a/README.md +++ b/README.md @@ -75,17 +75,13 @@ package main import ( "os" log "github.com/Sirupsen/logrus" - "github.com/Sirupsen/logrus/hooks/airbrake" + "github.com/gemnasium/logrus-airbrake-hook" ) func init() { // Log as JSON instead of the default ASCII formatter. log.SetFormatter(&log.JSONFormatter{}) - // Use the Airbrake hook to report errors that have Error severity or above to - // an exception tracker. You can create custom hooks, see the Hooks section. - log.AddHook(airbrake.NewHook("https://example.com", "xyz", "development")) - // Output to stderr instead of stdout, could also be a file. log.SetOutput(os.Stderr) @@ -182,13 +178,16 @@ Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in ```go import ( log "github.com/Sirupsen/logrus" - "github.com/Sirupsen/logrus/hooks/airbrake" + "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake" logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" "log/syslog" ) func init() { - log.AddHook(airbrake.NewHook("https://example.com", "xyz", "development")) + + // Use the Airbrake hook to report errors that have Error severity or above to + // an exception tracker. You can create custom hooks, see the Hooks section. + log.AddHook(airbrake.NewHook(123, "xyz", "production")) hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") if err != nil { @@ -202,7 +201,8 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | Hook | Description | | ----- | ----------- | -| [Airbrake](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go) | Send errors to an exception tracking service compatible with the Airbrake API. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. | +| [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/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go) | Send errors to the Papertrail 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/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go) | Send errors to the Bugsnag exception tracking service. | diff --git a/hooks/airbrake/airbrake.go b/hooks/airbrake/airbrake.go deleted file mode 100644 index b0502c335..000000000 --- a/hooks/airbrake/airbrake.go +++ /dev/null @@ -1,54 +0,0 @@ -package airbrake - -import ( - "errors" - "fmt" - - "github.com/Sirupsen/logrus" - "github.com/tobi/airbrake-go" -) - -// AirbrakeHook to send exceptions to an exception-tracking service compatible -// with the Airbrake API. -type airbrakeHook struct { - APIKey string - Endpoint string - Environment string -} - -func NewHook(endpoint, apiKey, env string) *airbrakeHook { - return &airbrakeHook{ - APIKey: apiKey, - Endpoint: endpoint, - Environment: env, - } -} - -func (hook *airbrakeHook) Fire(entry *logrus.Entry) error { - airbrake.ApiKey = hook.APIKey - airbrake.Endpoint = hook.Endpoint - airbrake.Environment = hook.Environment - - var notifyErr error - err, ok := entry.Data["error"].(error) - if ok { - notifyErr = err - } else { - notifyErr = errors.New(entry.Message) - } - - airErr := airbrake.Notify(notifyErr) - if airErr != nil { - return fmt.Errorf("Failed to send error to Airbrake: %s", airErr) - } - - return nil -} - -func (hook *airbrakeHook) Levels() []logrus.Level { - return []logrus.Level{ - logrus.ErrorLevel, - logrus.FatalLevel, - logrus.PanicLevel, - } -} diff --git a/hooks/airbrake/airbrake_test.go b/hooks/airbrake/airbrake_test.go deleted file mode 100644 index 058a91e34..000000000 --- a/hooks/airbrake/airbrake_test.go +++ /dev/null @@ -1,133 +0,0 @@ -package airbrake - -import ( - "encoding/xml" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/Sirupsen/logrus" -) - -type notice struct { - Error NoticeError `xml:"error"` -} -type NoticeError struct { - Class string `xml:"class"` - Message string `xml:"message"` -} - -type customErr struct { - msg string -} - -func (e *customErr) Error() string { - return e.msg -} - -const ( - testAPIKey = "abcxyz" - testEnv = "development" - expectedClass = "*airbrake.customErr" - expectedMsg = "foo" - unintendedMsg = "Airbrake will not see this string" -) - -var ( - noticeError = make(chan NoticeError, 1) -) - -// TestLogEntryMessageReceived checks if invoking Logrus' log.Error -// method causes an XML payload containing the log entry message is received -// by a HTTP server emulating an Airbrake-compatible endpoint. -func TestLogEntryMessageReceived(t *testing.T) { - log := logrus.New() - ts := startAirbrakeServer(t) - defer ts.Close() - - hook := NewHook(ts.URL, testAPIKey, "production") - log.Hooks.Add(hook) - - log.Error(expectedMsg) - - select { - case received := <-noticeError: - if received.Message != expectedMsg { - t.Errorf("Unexpected message received: %s", received.Message) - } - case <-time.After(time.Second): - t.Error("Timed out; no notice received by Airbrake API") - } -} - -// TestLogEntryMessageReceived confirms that, when passing an error type using -// logrus.Fields, a HTTP server emulating an Airbrake endpoint receives the -// error message returned by the Error() method on the error interface -// rather than the logrus.Entry.Message string. -func TestLogEntryWithErrorReceived(t *testing.T) { - log := logrus.New() - ts := startAirbrakeServer(t) - defer ts.Close() - - hook := NewHook(ts.URL, testAPIKey, "production") - log.Hooks.Add(hook) - - log.WithFields(logrus.Fields{ - "error": &customErr{expectedMsg}, - }).Error(unintendedMsg) - - select { - case received := <-noticeError: - if received.Message != expectedMsg { - t.Errorf("Unexpected message received: %s", received.Message) - } - if received.Class != expectedClass { - t.Errorf("Unexpected error class: %s", received.Class) - } - case <-time.After(time.Second): - t.Error("Timed out; no notice received by Airbrake API") - } -} - -// TestLogEntryWithNonErrorTypeNotReceived confirms that, when passing a -// non-error type using logrus.Fields, a HTTP server emulating an Airbrake -// endpoint receives the logrus.Entry.Message string. -// -// Only error types are supported when setting the 'error' field using -// logrus.WithFields(). -func TestLogEntryWithNonErrorTypeNotReceived(t *testing.T) { - log := logrus.New() - ts := startAirbrakeServer(t) - defer ts.Close() - - hook := NewHook(ts.URL, testAPIKey, "production") - log.Hooks.Add(hook) - - log.WithFields(logrus.Fields{ - "error": expectedMsg, - }).Error(unintendedMsg) - - select { - case received := <-noticeError: - if received.Message != unintendedMsg { - t.Errorf("Unexpected message received: %s", received.Message) - } - case <-time.After(time.Second): - t.Error("Timed out; no notice received by Airbrake API") - } -} - -func startAirbrakeServer(t *testing.T) *httptest.Server { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var notice notice - if err := xml.NewDecoder(r.Body).Decode(¬ice); err != nil { - t.Error(err) - } - r.Body.Close() - - noticeError <- notice.Error - })) - - return ts -} From 165e92c5a76c9f1bdd9a9d35822062517c048502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Lafoucrie=CC=80re?= Date: Tue, 6 Oct 2015 14:33:31 -0400 Subject: [PATCH 076/547] Remove leftover --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index b2b676a53..e23e701e2 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,6 @@ package main import ( "os" log "github.com/Sirupsen/logrus" - "github.com/gemnasium/logrus-airbrake-hook" ) func init() { From 73cd60e423bfd6d6f03289929b608d988de7941a Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 6 Oct 2015 18:39:39 +0000 Subject: [PATCH 077/547] changelog: add airbrake note --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d57f6bcd3..2eb2dd465 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # 0.9.0 (Unreleased) * logrus/text_formatter: don't emit empty msg +* logrus/hooks/airbrake: move out of main repository # 0.8.7 From 4359f51fa3dcbf38e793b652194f60ef009a1ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Lafoucrie=CC=80re?= Date: Tue, 6 Oct 2015 14:43:55 -0400 Subject: [PATCH 078/547] Move graylog to dedicated repo The new repo uses gopkg.in for versioning. This is the continuation of logrus moving hooks to dedicated repos. The old repo https://github.com/gemnasium/logrus-hooks/tree/master/graylog is deprecated, as we don't want to share things there too (import airbrake while I just want graylog). It will be removed in a few months. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e23e701e2..244a52dbb 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,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/) | | [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-hooks/tree/master/graylog) | Hook for logging to [Graylog](http://graylog2.org/) | +| [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 | From 2d72369d40e7065a090cfa22340f0e8d6a6e5a87 Mon Sep 17 00:00:00 2001 From: Austin Riendeau Date: Tue, 6 Oct 2015 14:01:18 -0600 Subject: [PATCH 079/547] Fixes breaking change with removing airbrake. --- examples/hook/hook.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/hook/hook.go b/examples/hook/hook.go index cb5759a35..4e30c7dcd 100644 --- a/examples/hook/hook.go +++ b/examples/hook/hook.go @@ -2,7 +2,7 @@ package main import ( "github.com/Sirupsen/logrus" - "github.com/Sirupsen/logrus/hooks/airbrake" + "github.com/gemnasium/logrus-airbrake-hook" ) var log = logrus.New() From 5b516ef1698cf1c1ce50e5450233950c46fcef4a Mon Sep 17 00:00:00 2001 From: Austin Riendeau Date: Tue, 6 Oct 2015 14:24:51 -0600 Subject: [PATCH 080/547] hook.go matches their gopkg.in/gemnasium/logrus-airbrake-hook.v2 now --- examples/hook/hook.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/hook/hook.go b/examples/hook/hook.go index 4e30c7dcd..3187f6d3e 100644 --- a/examples/hook/hook.go +++ b/examples/hook/hook.go @@ -2,14 +2,14 @@ package main import ( "github.com/Sirupsen/logrus" - "github.com/gemnasium/logrus-airbrake-hook" + "gopkg.in/gemnasium/logrus-airbrake-hook.v2" ) var log = logrus.New() func init() { log.Formatter = new(logrus.TextFormatter) // default - log.Hooks.Add(airbrake.NewHook("https://example.com", "xyz", "development")) + log.Hooks.Add(airbrake.NewHook(123, "xyz", "development")) } func main() { From 455d314475c4a0868c75fe4c5a095bc5a928a891 Mon Sep 17 00:00:00 2001 From: evalphobia Date: Wed, 7 Oct 2015 12:59:33 +0900 Subject: [PATCH 081/547] Move sentry hook to external repository --- README.md | 2 +- hooks/sentry/README.md | 111 -------------------------- hooks/sentry/sentry.go | 137 -------------------------------- hooks/sentry/sentry_test.go | 154 ------------------------------------ 4 files changed, 1 insertion(+), 403 deletions(-) delete mode 100644 hooks/sentry/README.md delete mode 100644 hooks/sentry/sentry.go delete mode 100644 hooks/sentry/sentry_test.go diff --git a/README.md b/README.md index 244a52dbb..3e526c1bb 100644 --- a/README.md +++ b/README.md @@ -205,7 +205,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [Papertrail](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go) | Send errors to the Papertrail 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/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go) | Send errors to the Bugsnag exception tracking service. | -| [Sentry](https://github.com/Sirupsen/logrus/blob/master/hooks/sentry/sentry.go) | Send errors to the Sentry error logging and aggregation 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. | diff --git a/hooks/sentry/README.md b/hooks/sentry/README.md deleted file mode 100644 index 31de6540a..000000000 --- a/hooks/sentry/README.md +++ /dev/null @@ -1,111 +0,0 @@ -# Sentry Hook for Logrus :walrus: - -[Sentry](https://getsentry.com) provides both self-hosted and hosted -solutions for exception tracking. -Both client and server are -[open source](https://github.com/getsentry/sentry). - -## Usage - -Every sentry application defined on the server gets a different -[DSN](https://www.getsentry.com/docs/). In the example below replace -`YOUR_DSN` with the one created for your application. - -```go -import ( - "github.com/Sirupsen/logrus" - "github.com/Sirupsen/logrus/hooks/sentry" -) - -func main() { - log := logrus.New() - hook, err := logrus_sentry.NewSentryHook(YOUR_DSN, []logrus.Level{ - logrus.PanicLevel, - logrus.FatalLevel, - logrus.ErrorLevel, - }) - - if err == nil { - log.Hooks.Add(hook) - } -} -``` - -If you wish to initialize a SentryHook with tags, you can use the `NewWithTagsSentryHook` constructor to provide default tags: - -```go -tags := map[string]string{ - "site": "example.com", -} -levels := []logrus.Level{ - logrus.PanicLevel, - logrus.FatalLevel, - logrus.ErrorLevel, -} -hook, err := logrus_sentry.NewWithTagsSentryHook(YOUR_DSN, tags, levels) - -``` - -If you wish to initialize a SentryHook with an already initialized raven client, you can use -the `NewWithClientSentryHook` constructor: - -```go -import ( - "github.com/Sirupsen/logrus" - "github.com/Sirupsen/logrus/hooks/sentry" - "github.com/getsentry/raven-go" -) - -func main() { - log := logrus.New() - - client, err := raven.New(YOUR_DSN) - if err != nil { - log.Fatal(err) - } - - hook, err := logrus_sentry.NewWithClientSentryHook(client, []logrus.Level{ - logrus.PanicLevel, - logrus.FatalLevel, - logrus.ErrorLevel, - }) - - if err == nil { - log.Hooks.Add(hook) - } -} - -hook, err := NewWithClientSentryHook(client, []logrus.Level{ - logrus.ErrorLevel, -}) -``` - -## Special fields - -Some logrus fields have a special meaning in this hook, -these are `server_name`, `logger` and `http_request`. -When logs are sent to sentry these fields are treated differently. -- `server_name` (also known as hostname) is the name of the server which -is logging the event (hostname.example.com) -- `logger` is the part of the application which is logging the event. -In go this usually means setting it to the name of the package. -- `http_request` is the in-coming request(*http.Request). The detailed request data are sent to Sentry. - -## Timeout - -`Timeout` is the time the sentry hook will wait for a response -from the sentry server. - -If this time elapses with no response from -the server an error will be returned. - -If `Timeout` is set to 0 the SentryHook will not wait for a reply -and will assume a correct delivery. - -The SentryHook has a default timeout of `100 milliseconds` when created -with a call to `NewSentryHook`. This can be changed by assigning a value to the `Timeout` field: - -```go -hook, _ := logrus_sentry.NewSentryHook(...) -hook.Timeout = 20*time.Second -``` diff --git a/hooks/sentry/sentry.go b/hooks/sentry/sentry.go deleted file mode 100644 index cf88098a8..000000000 --- a/hooks/sentry/sentry.go +++ /dev/null @@ -1,137 +0,0 @@ -package logrus_sentry - -import ( - "fmt" - "net/http" - "time" - - "github.com/Sirupsen/logrus" - "github.com/getsentry/raven-go" -) - -var ( - severityMap = map[logrus.Level]raven.Severity{ - logrus.DebugLevel: raven.DEBUG, - logrus.InfoLevel: raven.INFO, - logrus.WarnLevel: raven.WARNING, - logrus.ErrorLevel: raven.ERROR, - logrus.FatalLevel: raven.FATAL, - logrus.PanicLevel: raven.FATAL, - } -) - -func getAndDel(d logrus.Fields, key string) (string, bool) { - var ( - ok bool - v interface{} - val string - ) - if v, ok = d[key]; !ok { - return "", false - } - - if val, ok = v.(string); !ok { - return "", false - } - delete(d, key) - return val, true -} - -func getAndDelRequest(d logrus.Fields, key string) (*http.Request, bool) { - var ( - ok bool - v interface{} - req *http.Request - ) - if v, ok = d[key]; !ok { - return nil, false - } - if req, ok = v.(*http.Request); !ok || req == nil { - return nil, false - } - delete(d, key) - return req, true -} - -// SentryHook delivers logs to a sentry server. -type SentryHook struct { - // Timeout sets the time to wait for a delivery error from the sentry server. - // If this is set to zero the server will not wait for any response and will - // consider the message correctly sent - Timeout time.Duration - - client *raven.Client - levels []logrus.Level -} - -// NewSentryHook creates a hook to be added to an instance of logger -// and initializes the raven client. -// This method sets the timeout to 100 milliseconds. -func NewSentryHook(DSN string, levels []logrus.Level) (*SentryHook, error) { - client, err := raven.New(DSN) - if err != nil { - return nil, err - } - return &SentryHook{100 * time.Millisecond, client, levels}, nil -} - -// NewWithTagsSentryHook creates a hook with tags to be added to an instance -// of logger and initializes the raven client. This method sets the timeout to -// 100 milliseconds. -func NewWithTagsSentryHook(DSN string, tags map[string]string, levels []logrus.Level) (*SentryHook, error) { - client, err := raven.NewWithTags(DSN, tags) - if err != nil { - return nil, err - } - return &SentryHook{100 * time.Millisecond, client, levels}, nil -} - -// NewWithClientSentryHook creates a hook using an initialized raven client. -// This method sets the timeout to 100 milliseconds. -func NewWithClientSentryHook(client *raven.Client, levels []logrus.Level) (*SentryHook, error) { - return &SentryHook{100 * time.Millisecond, client, levels}, nil -} - -// Called when an event should be sent to sentry -// Special fields that sentry uses to give more information to the server -// are extracted from entry.Data (if they are found) -// These fields are: logger, server_name and http_request -func (hook *SentryHook) Fire(entry *logrus.Entry) error { - packet := &raven.Packet{ - Message: entry.Message, - Timestamp: raven.Timestamp(entry.Time), - Level: severityMap[entry.Level], - Platform: "go", - } - - d := entry.Data - - if logger, ok := getAndDel(d, "logger"); ok { - packet.Logger = logger - } - if serverName, ok := getAndDel(d, "server_name"); ok { - packet.ServerName = serverName - } - if req, ok := getAndDelRequest(d, "http_request"); ok { - packet.Interfaces = append(packet.Interfaces, raven.NewHttp(req)) - } - packet.Extra = map[string]interface{}(d) - - _, errCh := hook.client.Capture(packet, nil) - timeout := hook.Timeout - if timeout != 0 { - timeoutCh := time.After(timeout) - select { - case err := <-errCh: - return err - case <-timeoutCh: - return fmt.Errorf("no response from sentry server in %s", timeout) - } - } - return nil -} - -// Levels returns the available logging levels. -func (hook *SentryHook) Levels() []logrus.Level { - return hook.levels -} diff --git a/hooks/sentry/sentry_test.go b/hooks/sentry/sentry_test.go deleted file mode 100644 index 4a97bc63e..000000000 --- a/hooks/sentry/sentry_test.go +++ /dev/null @@ -1,154 +0,0 @@ -package logrus_sentry - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/http/httptest" - "reflect" - "strings" - "testing" - - "github.com/Sirupsen/logrus" - "github.com/getsentry/raven-go" -) - -const ( - message = "error message" - server_name = "testserver.internal" - logger_name = "test.logger" -) - -func getTestLogger() *logrus.Logger { - l := logrus.New() - l.Out = ioutil.Discard - return l -} - -func WithTestDSN(t *testing.T, tf func(string, <-chan *raven.Packet)) { - pch := make(chan *raven.Packet, 1) - s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - defer req.Body.Close() - d := json.NewDecoder(req.Body) - p := &raven.Packet{} - err := d.Decode(p) - if err != nil { - t.Fatal(err.Error()) - } - - pch <- p - })) - defer s.Close() - - fragments := strings.SplitN(s.URL, "://", 2) - dsn := fmt.Sprintf( - "%s://public:secret@%s/sentry/project-id", - fragments[0], - fragments[1], - ) - tf(dsn, pch) -} - -func TestSpecialFields(t *testing.T) { - WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) { - logger := getTestLogger() - - hook, err := NewSentryHook(dsn, []logrus.Level{ - logrus.ErrorLevel, - }) - - if err != nil { - t.Fatal(err.Error()) - } - logger.Hooks.Add(hook) - - req, _ := http.NewRequest("GET", "url", nil) - logger.WithFields(logrus.Fields{ - "server_name": server_name, - "logger": logger_name, - "http_request": req, - }).Error(message) - - packet := <-pch - if packet.Logger != logger_name { - t.Errorf("logger should have been %s, was %s", logger_name, packet.Logger) - } - - if packet.ServerName != server_name { - t.Errorf("server_name should have been %s, was %s", server_name, packet.ServerName) - } - }) -} - -func TestSentryHandler(t *testing.T) { - WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) { - logger := getTestLogger() - hook, err := NewSentryHook(dsn, []logrus.Level{ - logrus.ErrorLevel, - }) - if err != nil { - t.Fatal(err.Error()) - } - logger.Hooks.Add(hook) - - logger.Error(message) - packet := <-pch - if packet.Message != message { - t.Errorf("message should have been %s, was %s", message, packet.Message) - } - }) -} - -func TestSentryWithClient(t *testing.T) { - WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) { - logger := getTestLogger() - - client, _ := raven.New(dsn) - - hook, err := NewWithClientSentryHook(client, []logrus.Level{ - logrus.ErrorLevel, - }) - if err != nil { - t.Fatal(err.Error()) - } - logger.Hooks.Add(hook) - - logger.Error(message) - packet := <-pch - if packet.Message != message { - t.Errorf("message should have been %s, was %s", message, packet.Message) - } - }) -} - -func TestSentryTags(t *testing.T) { - WithTestDSN(t, func(dsn string, pch <-chan *raven.Packet) { - logger := getTestLogger() - tags := map[string]string{ - "site": "test", - } - levels := []logrus.Level{ - logrus.ErrorLevel, - } - - hook, err := NewWithTagsSentryHook(dsn, tags, levels) - if err != nil { - t.Fatal(err.Error()) - } - - logger.Hooks.Add(hook) - - logger.Error(message) - packet := <-pch - expected := raven.Tags{ - raven.Tag{ - Key: "site", - Value: "test", - }, - } - if !reflect.DeepEqual(packet.Tags, expected) { - t.Errorf("message should have been %s, was %s", message, packet.Message) - } - }) -} From c021bb107705113d6e81f9e0caec893fb58db1b9 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Wed, 7 Oct 2015 14:14:16 +0000 Subject: [PATCH 082/547] changelog: moved sentry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2eb2dd465..53616dbc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * logrus/text_formatter: don't emit empty msg * logrus/hooks/airbrake: move out of main repository +* logrus/hooks/sentry: move out of main repository # 0.8.7 From 533dad68d6cedb2fc85d0f6537e5f760fd7895c0 Mon Sep 17 00:00:00 2001 From: Peter Olds Date: Fri, 9 Oct 2015 10:28:56 -0600 Subject: [PATCH 083/547] Split out Papertrail hook to its own repo Signed-off-by: Peter Olds --- README.md | 2 +- hooks/papertrail/README.md | 28 --------------- hooks/papertrail/papertrail.go | 55 ----------------------------- hooks/papertrail/papertrail_test.go | 26 -------------- 4 files changed, 1 insertion(+), 110 deletions(-) delete mode 100644 hooks/papertrail/README.md delete mode 100644 hooks/papertrail/papertrail.go delete mode 100644 hooks/papertrail/papertrail_test.go diff --git a/README.md b/README.md index 3e526c1bb..50a3e9adb 100644 --- a/README.md +++ b/README.md @@ -202,7 +202,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. | | [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/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go) | Send errors to the Papertrail hosted logging service via UDP. | +| [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/Sirupsen/logrus/blob/master/hooks/bugsnag/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. | diff --git a/hooks/papertrail/README.md b/hooks/papertrail/README.md deleted file mode 100644 index ae61e9229..000000000 --- a/hooks/papertrail/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# Papertrail Hook for Logrus :walrus: - -[Papertrail](https://papertrailapp.com) provides hosted log management. Once stored in Papertrail, you can [group](http://help.papertrailapp.com/kb/how-it-works/groups/) your logs on various dimensions, [search](http://help.papertrailapp.com/kb/how-it-works/search-syntax) them, and trigger [alerts](http://help.papertrailapp.com/kb/how-it-works/alerts). - -In most deployments, you'll want to send logs to Papertrail via their [remote_syslog](http://help.papertrailapp.com/kb/configuration/configuring-centralized-logging-from-text-log-files-in-unix/) daemon, which requires no application-specific configuration. This hook is intended for relatively low-volume logging, likely in managed cloud hosting deployments where installing `remote_syslog` is not possible. - -## Usage - -You can find your Papertrail UDP port on your [Papertrail account page](https://papertrailapp.com/account/destinations). Substitute it below for `YOUR_PAPERTRAIL_UDP_PORT`. - -For `YOUR_APP_NAME`, substitute a short string that will readily identify your application or service in the logs. - -```go -import ( - "log/syslog" - "github.com/Sirupsen/logrus" - "github.com/Sirupsen/logrus/hooks/papertrail" -) - -func main() { - log := logrus.New() - hook, err := logrus_papertrail.NewPapertrailHook("logs.papertrailapp.com", YOUR_PAPERTRAIL_UDP_PORT, YOUR_APP_NAME) - - if err == nil { - log.Hooks.Add(hook) - } -} -``` diff --git a/hooks/papertrail/papertrail.go b/hooks/papertrail/papertrail.go deleted file mode 100644 index c0f10c1bd..000000000 --- a/hooks/papertrail/papertrail.go +++ /dev/null @@ -1,55 +0,0 @@ -package logrus_papertrail - -import ( - "fmt" - "net" - "os" - "time" - - "github.com/Sirupsen/logrus" -) - -const ( - format = "Jan 2 15:04:05" -) - -// PapertrailHook to send logs to a logging service compatible with the Papertrail API. -type PapertrailHook struct { - Host string - Port int - AppName string - UDPConn net.Conn -} - -// NewPapertrailHook creates a hook to be added to an instance of logger. -func NewPapertrailHook(host string, port int, appName string) (*PapertrailHook, error) { - conn, err := net.Dial("udp", fmt.Sprintf("%s:%d", host, port)) - return &PapertrailHook{host, port, appName, conn}, err -} - -// Fire is called when a log event is fired. -func (hook *PapertrailHook) Fire(entry *logrus.Entry) error { - date := time.Now().Format(format) - msg, _ := entry.String() - payload := fmt.Sprintf("<22> %s %s: %s", date, hook.AppName, msg) - - bytesWritten, err := hook.UDPConn.Write([]byte(payload)) - if err != nil { - fmt.Fprintf(os.Stderr, "Unable to send log line to Papertrail via UDP. Wrote %d bytes before error: %v", bytesWritten, err) - return err - } - - return nil -} - -// Levels returns the available logging levels. -func (hook *PapertrailHook) Levels() []logrus.Level { - return []logrus.Level{ - logrus.PanicLevel, - logrus.FatalLevel, - logrus.ErrorLevel, - logrus.WarnLevel, - logrus.InfoLevel, - logrus.DebugLevel, - } -} diff --git a/hooks/papertrail/papertrail_test.go b/hooks/papertrail/papertrail_test.go deleted file mode 100644 index 96318d003..000000000 --- a/hooks/papertrail/papertrail_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package logrus_papertrail - -import ( - "fmt" - "testing" - - "github.com/Sirupsen/logrus" - "github.com/stvp/go-udp-testing" -) - -func TestWritingToUDP(t *testing.T) { - port := 16661 - udp.SetAddr(fmt.Sprintf(":%d", port)) - - hook, err := NewPapertrailHook("localhost", port, "test") - if err != nil { - t.Errorf("Unable to connect to local UDP server.") - } - - log := logrus.New() - log.Hooks.Add(hook) - - udp.ShouldReceive(t, "foo", func() { - log.Info("foo") - }) -} From 2f596e91f6c04c3a0f01cebe5093867c0e80b1eb Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Fri, 9 Oct 2015 17:37:01 +0000 Subject: [PATCH 084/547] changelog: move papertrail --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53616dbc5..ce45d541a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * logrus/text_formatter: don't emit empty msg * logrus/hooks/airbrake: move out of main repository * logrus/hooks/sentry: move out of main repository +* logrus/hooks/papertrail: move out of main repository # 0.8.7 From dde3c44643f7fc7fbec7be5175cbf9fe5fac4fc2 Mon Sep 17 00:00:00 2001 From: Abramovic Date: Mon, 12 Oct 2015 10:41:37 -0700 Subject: [PATCH 085/547] include InfluxDB hook --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 50a3e9adb..996c37659 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [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 | +| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb | #### Level logging From 75b5259873dcac1d2adf67bda02f0961c9471dc0 Mon Sep 17 00:00:00 2001 From: Zeal Date: Mon, 19 Oct 2015 14:19:45 +0800 Subject: [PATCH 086/547] add tools section for logrus mate --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 996c37659..fdd4c66da 100644 --- a/README.md +++ b/README.md @@ -353,5 +353,10 @@ Log rotation is not provided with Logrus. Log rotation should be done by an external program (like `logrotate(8)`) that can compress and delete old log entries. It should not be a feature of the application-level logger. +#### Tools + +| Tool | Description | +| ---- | ----------- | +|[Logrus Mate](github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to management loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.| [godoc]: https://godoc.org/github.com/Sirupsen/logrus From fd2b0f5cb1b086f24d10f1c808f0c5f36e7f0dd2 Mon Sep 17 00:00:00 2001 From: Scott Francis Date: Thu, 22 Oct 2015 20:20:45 +0000 Subject: [PATCH 087/547] Remove Bugsnag --- README.md | 2 +- hooks/bugsnag/bugsnag.go | 68 ----------------------------------- hooks/bugsnag/bugsnag_test.go | 64 --------------------------------- 3 files changed, 1 insertion(+), 133 deletions(-) delete mode 100644 hooks/bugsnag/bugsnag.go delete mode 100644 hooks/bugsnag/bugsnag_test.go diff --git a/README.md b/README.md index 996c37659..03bd08c0f 100644 --- a/README.md +++ b/README.md @@ -204,7 +204,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. | | [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/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go) | Send errors to the Bugsnag exception tracking service. | +| [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/) | diff --git a/hooks/bugsnag/bugsnag.go b/hooks/bugsnag/bugsnag.go deleted file mode 100644 index d20a0f54a..000000000 --- a/hooks/bugsnag/bugsnag.go +++ /dev/null @@ -1,68 +0,0 @@ -package logrus_bugsnag - -import ( - "errors" - - "github.com/Sirupsen/logrus" - "github.com/bugsnag/bugsnag-go" -) - -type bugsnagHook struct{} - -// ErrBugsnagUnconfigured is returned if NewBugsnagHook is called before -// bugsnag.Configure. Bugsnag must be configured before the hook. -var ErrBugsnagUnconfigured = errors.New("bugsnag must be configured before installing this logrus hook") - -// ErrBugsnagSendFailed indicates that the hook failed to submit an error to -// bugsnag. The error was successfully generated, but `bugsnag.Notify()` -// failed. -type ErrBugsnagSendFailed struct { - err error -} - -func (e ErrBugsnagSendFailed) Error() string { - return "failed to send error to Bugsnag: " + e.err.Error() -} - -// NewBugsnagHook initializes a logrus hook which sends exceptions to an -// exception-tracking service compatible with the Bugsnag API. Before using -// this hook, you must call bugsnag.Configure(). The returned object should be -// registered with a log via `AddHook()` -// -// Entries that trigger an Error, Fatal or Panic should now include an "error" -// field to send to Bugsnag. -func NewBugsnagHook() (*bugsnagHook, error) { - if bugsnag.Config.APIKey == "" { - return nil, ErrBugsnagUnconfigured - } - return &bugsnagHook{}, nil -} - -// Fire forwards an error to Bugsnag. Given a logrus.Entry, it extracts the -// "error" field (or the Message if the error isn't present) and sends it off. -func (hook *bugsnagHook) Fire(entry *logrus.Entry) error { - var notifyErr error - err, ok := entry.Data["error"].(error) - if ok { - notifyErr = err - } else { - notifyErr = errors.New(entry.Message) - } - - bugsnagErr := bugsnag.Notify(notifyErr) - if bugsnagErr != nil { - return ErrBugsnagSendFailed{bugsnagErr} - } - - return nil -} - -// Levels enumerates the log levels on which the error should be forwarded to -// bugsnag: everything at or above the "Error" level. -func (hook *bugsnagHook) Levels() []logrus.Level { - return []logrus.Level{ - logrus.ErrorLevel, - logrus.FatalLevel, - logrus.PanicLevel, - } -} diff --git a/hooks/bugsnag/bugsnag_test.go b/hooks/bugsnag/bugsnag_test.go deleted file mode 100644 index e9ea298d8..000000000 --- a/hooks/bugsnag/bugsnag_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package logrus_bugsnag - -import ( - "encoding/json" - "errors" - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/Sirupsen/logrus" - "github.com/bugsnag/bugsnag-go" -) - -type notice struct { - Events []struct { - Exceptions []struct { - Message string `json:"message"` - } `json:"exceptions"` - } `json:"events"` -} - -func TestNoticeReceived(t *testing.T) { - msg := make(chan string, 1) - expectedMsg := "foo" - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var notice notice - data, _ := ioutil.ReadAll(r.Body) - if err := json.Unmarshal(data, ¬ice); err != nil { - t.Error(err) - } - _ = r.Body.Close() - - msg <- notice.Events[0].Exceptions[0].Message - })) - defer ts.Close() - - hook := &bugsnagHook{} - - bugsnag.Configure(bugsnag.Configuration{ - Endpoint: ts.URL, - ReleaseStage: "production", - APIKey: "12345678901234567890123456789012", - Synchronous: true, - }) - - log := logrus.New() - log.Hooks.Add(hook) - - log.WithFields(logrus.Fields{ - "error": errors.New(expectedMsg), - }).Error("Bugsnag will not see this string") - - select { - case received := <-msg: - if received != expectedMsg { - t.Errorf("Unexpected message received: %s", received) - } - case <-time.After(time.Second): - t.Error("Timed out; no notice received by Bugsnag API") - } -} From 0e45811bf97915a79db3d4078d2cc5070e257ea0 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 22 Oct 2015 17:52:25 -0400 Subject: [PATCH 088/547] changelog: remove bugsnag --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce45d541a..ecc843272 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * logrus/hooks/airbrake: move out of main repository * logrus/hooks/sentry: move out of main repository * logrus/hooks/papertrail: move out of main repository +* logrus/hooks/bugsnag: move out of main repository # 0.8.7 From 174f4e79cf632023341558bf3c044b0037208201 Mon Sep 17 00:00:00 2001 From: Gert-Jan Timmer Date: Tue, 27 Oct 2015 10:18:45 +0100 Subject: [PATCH 089/547] Added Build Rule Add build rule, to only build on Linux platform, reason for adding this: This allows the usage of the logrus package within a Windows based project. Current logrus project fails to import due to the fact the syslog is missing on Windows. --- hooks/syslog/syslog.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hooks/syslog/syslog.go b/hooks/syslog/syslog.go index b6fa37462..c59f331d1 100644 --- a/hooks/syslog/syslog.go +++ b/hooks/syslog/syslog.go @@ -1,3 +1,5 @@ +// +build !windows,!nacl,!plan9 + package logrus_syslog import ( From 7d6516fc9083be9aaa8c3d6588a7ccafd0ed677f Mon Sep 17 00:00:00 2001 From: JoongSeob Vito Kim Date: Tue, 27 Oct 2015 18:58:00 +0900 Subject: [PATCH 090/547] Add octokit hook. Add octokit(github) hook. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 03bd08c0f..53d27d449 100644 --- a/README.md +++ b/README.md @@ -219,6 +219,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd | | [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb | | [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 | #### Level logging From 0d5a780fdea74ac9981ade5a211fbb8ee8d20b8b Mon Sep 17 00:00:00 2001 From: Denis Parchenko Date: Tue, 27 Oct 2015 01:53:47 +0200 Subject: [PATCH 091/547] Add ref to third-party prefixed log formatter --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 03bd08c0f..5f49077e9 100644 --- a/README.md +++ b/README.md @@ -304,7 +304,8 @@ The built-in logging formatters are: Third party logging formatters: -* [`zalgo`](https://github.com/aybabtme/logzalgo): invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. +* [`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̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. You can define your formatter by implementing the `Formatter` interface, requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a From ea7c0096f78a005f49005f5a015a7004e11884fd Mon Sep 17 00:00:00 2001 From: 0xe0f Date: Tue, 3 Nov 2015 12:21:35 +0300 Subject: [PATCH 092/547] Added implementation of function "IsTerminal" for Solaris --- terminal_solaris.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 terminal_solaris.go diff --git a/terminal_solaris.go b/terminal_solaris.go new file mode 100644 index 000000000..3e70bf7bf --- /dev/null +++ b/terminal_solaris.go @@ -0,0 +1,15 @@ +// +build solaris + +package logrus + +import ( + "os" + + "golang.org/x/sys/unix" +) + +// 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 +} From 3d3f4f112b6321aedc168968850d6f970a1a901f Mon Sep 17 00:00:00 2001 From: Ian Eyberg Date: Fri, 6 Nov 2015 14:47:10 -0800 Subject: [PATCH 093/547] adding deferpanic hook in readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 53d27d449..a3a3d42d9 100644 --- a/README.md +++ b/README.md @@ -220,6 +220,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb | | [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 | #### Level logging From 62393b4c196b197e358563cea859786464061c14 Mon Sep 17 00:00:00 2001 From: Zeal Date: Mon, 16 Nov 2015 18:22:07 +0800 Subject: [PATCH 094/547] fix logrus_mate link address --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f1e2ef0af..ddfe7959d 100644 --- a/README.md +++ b/README.md @@ -360,6 +360,6 @@ entries. It should not be a feature of the application-level logger. | Tool | Description | | ---- | ----------- | -|[Logrus Mate](github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to management loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.| +|[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.| [godoc]: https://godoc.org/github.com/Sirupsen/logrus From 188d3b0ec8b07365579e99e1ed78ddb7b955a409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Sat, 21 Nov 2015 17:51:52 +0100 Subject: [PATCH 095/547] formatter.go: simplify prefixFieldClashes(Fields) --- formatter.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/formatter.go b/formatter.go index 104d689f1..b5fbe934d 100644 --- a/formatter.go +++ b/formatter.go @@ -31,18 +31,15 @@ type Formatter interface { // It's not exported because it's still using Data in an opinionated way. It's to // avoid code duplication between the two default formatters. func prefixFieldClashes(data Fields) { - _, ok := data["time"] - if ok { - data["fields.time"] = data["time"] + if t, ok := data["time"]; ok { + data["fields.time"] = t } - _, ok = data["msg"] - if ok { - data["fields.msg"] = data["msg"] + if m, ok := data["msg"]; ok { + data["fields.msg"] = m } - _, ok = data["level"] - if ok { - data["fields.level"] = data["level"] + if l, ok := data["level"]; ok { + data["fields.level"] = l } } From ed6048b39308fbc27d27e9d2056d82063679a8bf Mon Sep 17 00:00:00 2001 From: Graham Hughes Date: Sat, 21 Nov 2015 22:59:38 -0800 Subject: [PATCH 096/547] Support `WithError` on `Logger`. Should address #277. --- logger.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/logger.go b/logger.go index fd9804c64..2fdb23176 100644 --- a/logger.go +++ b/logger.go @@ -64,6 +64,12 @@ func (logger *Logger) WithFields(fields Fields) *Entry { return NewEntry(logger).WithFields(fields) } +// Add an error as single field to the log entry. All it does is call +// `WithError` for the given `error`. +func (logger *Logger) WithError(err error) *Entry { + return NewEntry(logger).WithError(err) +} + func (logger *Logger) Debugf(format string, args ...interface{}) { if logger.Level >= DebugLevel { NewEntry(logger).Debugf(format, args...) From 4aeda966a171d20c6e74a57a5b6f5c6e3985a4d1 Mon Sep 17 00:00:00 2001 From: mitsuteru sawa Date: Mon, 30 Nov 2015 16:22:28 +0900 Subject: [PATCH 097/547] Fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit “ should be ". --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ddfe7959d..55d3a8d5f 100644 --- a/README.md +++ b/README.md @@ -301,7 +301,7 @@ The built-in logging formatters are: * `logrus/formatters/logstash.LogstashFormatter`. Logs fields as [Logstash](http://logstash.net) Events. ```go - logrus.SetFormatter(&logstash.LogstashFormatter{Type: “application_name"}) + logrus.SetFormatter(&logstash.LogstashFormatter{Type: "application_name"}) ``` Third party logging formatters: From c2f8388511050a3a21201d6bce3b13d753ad059f Mon Sep 17 00:00:00 2001 From: Daniel Wagner-Hall Date: Thu, 3 Dec 2015 12:58:31 +0000 Subject: [PATCH 098/547] Detect TTY based on stderr, not stdout We actually write to stderr by default, so: bin >/dev/null currently weirdly prints non-colorized output, whereas: bin 2>log weirdly prints colorized output to a file. --- terminal_notwindows.go | 4 ++-- terminal_windows.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/terminal_notwindows.go b/terminal_notwindows.go index 4bb537602..b343b3a37 100644 --- a/terminal_notwindows.go +++ b/terminal_notwindows.go @@ -12,9 +12,9 @@ import ( "unsafe" ) -// IsTerminal returns true if the given file descriptor is a terminal. +// IsTerminal returns true if stderr's file descriptor is a terminal. func IsTerminal() bool { - fd := syscall.Stdout + fd := syscall.Stderr var termios Termios _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) return err == 0 diff --git a/terminal_windows.go b/terminal_windows.go index 2e09f6f7e..0146845d1 100644 --- a/terminal_windows.go +++ b/terminal_windows.go @@ -18,9 +18,9 @@ var ( procGetConsoleMode = kernel32.NewProc("GetConsoleMode") ) -// IsTerminal returns true if the given file descriptor is a terminal. +// IsTerminal returns true if stderr's file descriptor is a terminal. func IsTerminal() bool { - fd := syscall.Stdout + 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 From 6c590fd040c82e93641b7f309fd5057397264646 Mon Sep 17 00:00:00 2001 From: Brad Brown Date: Mon, 18 Jan 2016 17:25:58 +1300 Subject: [PATCH 099/547] avoid modifying the entry by copying all fields and values --- formatters/logstash/logstash.go | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/formatters/logstash/logstash.go b/formatters/logstash/logstash.go index 8ea93ddf2..aad646abf 100644 --- a/formatters/logstash/logstash.go +++ b/formatters/logstash/logstash.go @@ -17,38 +17,43 @@ type LogstashFormatter struct { } func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) { - entry.Data["@version"] = 1 + fields := make(logrus.Fields) + for k, v := range entry.Data { + fields[k] = v + } + + fields["@version"] = 1 if f.TimestampFormat == "" { f.TimestampFormat = logrus.DefaultTimestampFormat } - entry.Data["@timestamp"] = entry.Time.Format(f.TimestampFormat) + fields["@timestamp"] = entry.Time.Format(f.TimestampFormat) // set message field v, ok := entry.Data["message"] if ok { - entry.Data["fields.message"] = v + fields["fields.message"] = v } - entry.Data["message"] = entry.Message + fields["message"] = entry.Message // set level field v, ok = entry.Data["level"] if ok { - entry.Data["fields.level"] = v + fields["fields.level"] = v } - entry.Data["level"] = entry.Level.String() + fields["level"] = entry.Level.String() // set type field if f.Type != "" { v, ok = entry.Data["type"] if ok { - entry.Data["fields.type"] = v + fields["fields.type"] = v } - entry.Data["type"] = f.Type + fields["type"] = f.Type } - serialized, err := json.Marshal(entry.Data) + serialized, err := json.Marshal(fields) if err != nil { return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) } From 66f4b92e42cf9dd2f61ef11488f606af4188479b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ronny=20L=C3=B3pez?= Date: Sat, 23 Jan 2016 10:54:36 +0100 Subject: [PATCH 100/547] Run tests with -race --- .travis.yml | 2 ++ logrus_test.go | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/.travis.yml b/.travis.yml index ec6411425..ff23150dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,8 @@ language: go go: - 1.3 - 1.4 + - 1.5 - tip install: - go get -t ./... +script: GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v ./... diff --git a/logrus_test.go b/logrus_test.go index efaacea23..b7d9302df 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -299,3 +299,18 @@ func TestGetSetLevelRace(t *testing.T) { } wg.Wait() } + +func TestLoggingRace(t *testing.T) { + logger := New() + + var wg sync.WaitGroup + wg.Add(100) + + for i := 0; i < 100; i++ { + go func() { + logger.Info("info") + wg.Done() + }() + } + wg.Wait() +} From 4f5b0925cb9bf9ae3a2277218624c0bf931314dd Mon Sep 17 00:00:00 2001 From: Rogier Lommers Date: Tue, 26 Jan 2016 21:01:36 +0100 Subject: [PATCH 101/547] add link to logrus-redis-hook --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 55d3a8d5f..f8720c9a7 100644 --- a/README.md +++ b/README.md @@ -221,6 +221,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 | | [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) | #### Level logging From ed6247f6a7429dc774a6f302428bf9ebf15914e5 Mon Sep 17 00:00:00 2001 From: Roma Sokolov Date: Mon, 1 Feb 2016 16:55:14 +0000 Subject: [PATCH 102/547] Add LogrusLogger interface for Entry and Logger This make it possible for client code to accept either Logger or Entry. For example, utility function may accept logger object to inform fatal errors and it is job of the calling code to provide either generic top-level logger, or request-bound Entry created using .WithFields. (fixes #308) --- logrus.go | 36 ++++++++++++++++++++++++++++++++++++ logrus_test.go | 17 +++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/logrus.go b/logrus.go index 0c09fbc26..aa5fa7e82 100644 --- a/logrus.go +++ b/logrus.go @@ -96,3 +96,39 @@ type StdLogger interface { Panicf(string, ...interface{}) Panicln(...interface{}) } + +// Logrus logger interface generalizes Entry and Logger types, so you can take any of these +type LogrusLogger interface { + // we can return LogrusLogger here, but this will require many changes and will + // possible break backward compatiblity + WithField(key string, value interface{}) *Entry + WithFields(fields Fields) *Entry + WithError(err error) *Entry + + Debugf(format string, args ...interface{}) + Infof(format string, args ...interface{}) + Printf(format string, args ...interface{}) + Warnf(format string, args ...interface{}) + Warningf(format string, args ...interface{}) + Errorf(format string, args ...interface{}) + Fatalf(format string, args ...interface{}) + Panicf(format string, args ...interface{}) + + Debug(args ...interface{}) + Info(args ...interface{}) + Print(args ...interface{}) + Warn(args ...interface{}) + Warning(args ...interface{}) + Error(args ...interface{}) + Fatal(args ...interface{}) + Panic(args ...interface{}) + + Debugln(args ...interface{}) + Infoln(args ...interface{}) + Println(args ...interface{}) + Warnln(args ...interface{}) + Warningln(args ...interface{}) + Errorln(args ...interface{}) + Fatalln(args ...interface{}) + Panicln(args ...interface{}) +} diff --git a/logrus_test.go b/logrus_test.go index b7d9302df..4bc1268e0 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -314,3 +314,20 @@ func TestLoggingRace(t *testing.T) { } wg.Wait() } + +// Compile test +func TestLogrusInterface(t *testing.T) { + var buffer bytes.Buffer + fn := func(l LogrusLogger) { + b := l.WithField("key", "value") + b.Debug("Test") + } + // test logger + logger := New() + logger.Out = &buffer + fn(logger) + + // test Entry + e := logger.WithField("another", "value") + fn(e) +} From 8ebf0b576ee1b56c2e4056a2ac6d45ce681e3473 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 2 Feb 2016 21:14:36 +0000 Subject: [PATCH 103/547] changelog: bump to 0.9.0 --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecc843272..9e9e6009a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,14 @@ -# 0.9.0 (Unreleased) +# 0.9.0 * logrus/text_formatter: don't emit empty msg * logrus/hooks/airbrake: move out of main repository * logrus/hooks/sentry: move out of main repository * logrus/hooks/papertrail: move out of main repository * logrus/hooks/bugsnag: move out of main repository +* logrus/core: run tests with `-race` +* logrus/core: detect TTY based on `stderr` +* logrus/core: support `WithError` on logger +* logrus/core: Solaris support # 0.8.7 From 9344c28d6b665b3edc837919834c2d61172ec2eb Mon Sep 17 00:00:00 2001 From: Vlado Atanasov Date: Fri, 12 Feb 2016 10:30:30 +0000 Subject: [PATCH 104/547] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f8720c9a7..bf743cba6 100644 --- a/README.md +++ b/README.md @@ -222,6 +222,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [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) | +| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker | #### Level logging From a4555c4ecff0c63ff32ca2818cd861d6570c8440 Mon Sep 17 00:00:00 2001 From: Vlado Atanasov Date: Fri, 12 Feb 2016 10:30:56 +0000 Subject: [PATCH 105/547] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bf743cba6..2165f2694 100644 --- a/README.md +++ b/README.md @@ -222,7 +222,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [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) | -| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker | +| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) | #### Level logging From 53af7619d0f3f6fd1358e543ad7c0c19ca73fe3d Mon Sep 17 00:00:00 2001 From: spicydog Date: Tue, 16 Feb 2016 13:41:25 +1000 Subject: [PATCH 106/547] Update README.md Update set log formatter command to "log.SetFormatter()" since "log.Formatter = new()" does not work. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2165f2694..cb2fb41c7 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ plain text): ![Colored](http://i.imgur.com/PY7qMwd.png) -With `log.Formatter = new(logrus.JSONFormatter)`, for easy parsing by logstash +With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash or Splunk: ```json @@ -32,7 +32,7 @@ ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"} "time":"2014-03-10 19:57:38.562543128 -0400 EDT"} ``` -With the default `log.Formatter = new(&log.TextFormatter{})` when a TTY is not +With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not attached, the output is compatible with the [logfmt](http://godoc.org/github.com/kr/logfmt) format: From 775af24a168d63590357b0ad2b620688db27f83b Mon Sep 17 00:00:00 2001 From: Rickard Dybeck Date: Tue, 16 Feb 2016 16:27:38 +0100 Subject: [PATCH 107/547] Make ParseLevel case-insensitive Coming from an environment where loglevels are always specified in uppercase, having ParseLevel be case-insensitive is a nice to have. --- logrus.go | 3 ++- logrus_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/logrus.go b/logrus.go index 0c09fbc26..fa8a7cc28 100644 --- a/logrus.go +++ b/logrus.go @@ -3,6 +3,7 @@ package logrus import ( "fmt" "log" + "strings" ) // Fields type, used to pass to `WithFields`. @@ -33,7 +34,7 @@ func (level Level) String() string { // ParseLevel takes a string level and returns the Logrus log level constant. func ParseLevel(lvl string) (Level, error) { - switch lvl { + switch strings.ToLower(lvl) { case "panic": return PanicLevel, nil case "fatal": diff --git a/logrus_test.go b/logrus_test.go index b7d9302df..2cf03f2f3 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -255,30 +255,58 @@ func TestParseLevel(t *testing.T) { assert.Nil(t, err) assert.Equal(t, PanicLevel, l) + l, err = ParseLevel("PANIC") + assert.Nil(t, err) + assert.Equal(t, PanicLevel, l) + l, err = ParseLevel("fatal") assert.Nil(t, err) assert.Equal(t, FatalLevel, l) + l, err = ParseLevel("FATAL") + assert.Nil(t, err) + assert.Equal(t, FatalLevel, l) + l, err = ParseLevel("error") assert.Nil(t, err) assert.Equal(t, ErrorLevel, l) + l, err = ParseLevel("ERROR") + assert.Nil(t, err) + assert.Equal(t, ErrorLevel, l) + l, err = ParseLevel("warn") assert.Nil(t, err) assert.Equal(t, WarnLevel, l) + l, err = ParseLevel("WARN") + assert.Nil(t, err) + assert.Equal(t, WarnLevel, l) + l, err = ParseLevel("warning") assert.Nil(t, err) assert.Equal(t, WarnLevel, l) + l, err = ParseLevel("WARNING") + assert.Nil(t, err) + assert.Equal(t, WarnLevel, l) + l, err = ParseLevel("info") assert.Nil(t, err) assert.Equal(t, InfoLevel, l) + l, err = ParseLevel("INFO") + assert.Nil(t, err) + assert.Equal(t, InfoLevel, l) + l, err = ParseLevel("debug") assert.Nil(t, err) assert.Equal(t, DebugLevel, l) + l, err = ParseLevel("DEBUG") + assert.Nil(t, err) + assert.Equal(t, DebugLevel, l) + l, err = ParseLevel("invalid") assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error()) } From 67dbad25f84eb708acee5efd2d817f17720394b6 Mon Sep 17 00:00:00 2001 From: harshad Date: Wed, 17 Feb 2016 13:07:32 +0530 Subject: [PATCH 108/547] adding link for the kafka hook --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2165f2694..33a816302 100644 --- a/README.md +++ b/README.md @@ -223,6 +223,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 | | [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) | | [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) | +| [KafkaLogrus](https://github.com/Sirupsen/logrus) | Hook for logging to kafka | #### Level logging From bd3f4f75546b5a2a771c10f021ba11b96ea667a8 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 18 Feb 2016 19:38:25 -0500 Subject: [PATCH 109/547] readme: fix kafka link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 33a816302..c2aea0ed2 100644 --- a/README.md +++ b/README.md @@ -223,7 +223,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 | | [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) | | [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) | -| [KafkaLogrus](https://github.com/Sirupsen/logrus) | Hook for logging to kafka | +| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka | #### Level logging From 1f16a0c141a025c81720b158bfbee0d1cc813a3d Mon Sep 17 00:00:00 2001 From: Joern Barthel Date: Wed, 13 May 2015 14:12:36 +0200 Subject: [PATCH 110/547] Expose all levels (for hooks). --- hooks/syslog/syslog.go | 9 +-------- logrus.go | 10 ++++++++++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/hooks/syslog/syslog.go b/hooks/syslog/syslog.go index c59f331d1..a36e20032 100644 --- a/hooks/syslog/syslog.go +++ b/hooks/syslog/syslog.go @@ -50,12 +50,5 @@ func (hook *SyslogHook) Fire(entry *logrus.Entry) error { } func (hook *SyslogHook) Levels() []logrus.Level { - return []logrus.Level{ - logrus.PanicLevel, - logrus.FatalLevel, - logrus.ErrorLevel, - logrus.WarnLevel, - logrus.InfoLevel, - logrus.DebugLevel, - } + return logrus.AllLevels } diff --git a/logrus.go b/logrus.go index fa8a7cc28..1e9670d86 100644 --- a/logrus.go +++ b/logrus.go @@ -53,6 +53,16 @@ func ParseLevel(lvl string) (Level, error) { return l, fmt.Errorf("not a valid logrus Level: %q", lvl) } +// A constant exposing all logging levels +var AllLevels = []Level{ + PanicLevel, + FatalLevel, + ErrorLevel, + WarnLevel, + InfoLevel, + DebugLevel, +} + // These are the different logging levels. You can set the logging level to log // on your instance of logger, obtained with `logrus.New()`. const ( From 3f931a8446600a99e75eafab79bb440cd1355ad0 Mon Sep 17 00:00:00 2001 From: Joern Barthel Date: Wed, 13 May 2015 14:14:34 +0200 Subject: [PATCH 111/547] Added test hook. --- hooks/test/test.go | 67 +++++++++++++++++++++++++++++++++++++++++ hooks/test/test_test.go | 36 ++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 hooks/test/test.go create mode 100644 hooks/test/test_test.go diff --git a/hooks/test/test.go b/hooks/test/test.go new file mode 100644 index 000000000..068812535 --- /dev/null +++ b/hooks/test/test.go @@ -0,0 +1,67 @@ +package test + +import ( + "io/ioutil" + + "github.com/Sirupsen/logrus" +) + +// test.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. +func NewGlobal() *Hook { + + hook := new(Hook) + logrus.AddHook(hook) + + return hook + +} + +// Installs a test hook for a given local logger. +func NewLocal(logger *logrus.Logger) *Hook { + + hook := new(Hook) + logger.Hooks.Add(hook) + + return hook + +} + +// 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.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() (l *logrus.Entry) { + + if i := len(t.Entries) - 1; i < 0 { + return nil + } else { + return t.Entries[i] + } + +} + +// Reset removes all Entries from this test hook. +func (t *Hook) Reset() { + t.Entries = make([]*logrus.Entry, 0) +} diff --git a/hooks/test/test_test.go b/hooks/test/test_test.go new file mode 100644 index 000000000..fabe46fcd --- /dev/null +++ b/hooks/test/test_test.go @@ -0,0 +1,36 @@ +package test + +import ( + "testing" + + "github.com/Sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +func TestAllHooks(t *testing.T) { + + assert := assert.New(t) + + logger, hook := NewNullLogger() + + logger.Error("Hello error") + assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level) + assert.Equal("Hello error", hook.LastEntry().Message) + assert.Equal(1, len(hook.Entries)) + + logger.Warn("Hello warning") + assert.Equal(logrus.WarnLevel, hook.LastEntry().Level) + assert.Equal("Hello warning", hook.LastEntry().Message) + assert.Equal(2, len(hook.Entries)) + + hook.Reset() + assert.Equal(0, len(hook.Entries)) + + hook = NewGlobal() + + logrus.Error("Hello error") + assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level) + assert.Equal("Hello error", hook.LastEntry().Message) + assert.Equal(1, len(hook.Entries)) + +} From 3ba8299c8638137b91d9c69f3079681de087403d Mon Sep 17 00:00:00 2001 From: Joern Barthel Date: Fri, 9 Oct 2015 16:07:14 +0200 Subject: [PATCH 112/547] Extended test. --- hooks/test/test_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hooks/test/test_test.go b/hooks/test/test_test.go index fabe46fcd..d69455ba0 100644 --- a/hooks/test/test_test.go +++ b/hooks/test/test_test.go @@ -12,6 +12,8 @@ func TestAllHooks(t *testing.T) { assert := assert.New(t) logger, hook := NewNullLogger() + assert.Nil(hook.LastEntry()) + assert.Equal(0, len(hook.Entries)) logger.Error("Hello error") assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level) @@ -24,6 +26,7 @@ func TestAllHooks(t *testing.T) { assert.Equal(2, len(hook.Entries)) hook.Reset() + assert.Nil(hook.LastEntry()) assert.Equal(0, len(hook.Entries)) hook = NewGlobal() From 746fe398b93ab4be8f78ea1c8274bd8161429895 Mon Sep 17 00:00:00 2001 From: Joern Barthel Date: Fri, 9 Oct 2015 16:07:29 +0200 Subject: [PATCH 113/547] Added testing section to README. --- README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c2aea0ed2..2119b049e 100644 --- a/README.md +++ b/README.md @@ -365,4 +365,21 @@ entries. It should not be a feature of the application-level logger. | ---- | ----------- | |[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.| -[godoc]: https://godoc.org/github.com/Sirupsen/logrus +#### Testing + +Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides: + +* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook +* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any): + +```go +logger, hook := NewNullLogger() +logger.Error("Hello error") + +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()) +``` From 1f903c0ba2d249d3c0e54567b7309d31fafade03 Mon Sep 17 00:00:00 2001 From: Prateek Srivastava Date: Wed, 24 Feb 2016 14:05:45 -0700 Subject: [PATCH 114/547] Fix Godoc link Previous https://cloudup.com/cZnP3-jZ9O8 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2119b049e..d3a1d4cf7 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 reference](https://godoc.org/github.com/Sirupsen/logrus?status.png)][godoc] +# 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 From 0a580b8cff9901950c63ab28481643530bb5bdc0 Mon Sep 17 00:00:00 2001 From: dragon3 Date: Wed, 9 Mar 2016 23:13:41 -0500 Subject: [PATCH 115/547] Add Typetalk hook --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d3a1d4cf7..49f7f07cb 100644 --- a/README.md +++ b/README.md @@ -224,6 +224,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) | | [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/) | #### Level logging From 2c59ce4012e9dedb44ff0de67d679654bbea0920 Mon Sep 17 00:00:00 2001 From: Dimitrij Denissenko Date: Tue, 15 Mar 2016 07:39:40 +0000 Subject: [PATCH 116/547] Avoid re-allocations --- entry.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entry.go b/entry.go index 9ae900bc5..89e966e7b 100644 --- a/entry.go +++ b/entry.go @@ -68,7 +68,7 @@ func (entry *Entry) WithField(key string, value interface{}) *Entry { // Add a map of fields to the Entry. func (entry *Entry) WithFields(fields Fields) *Entry { - data := Fields{} + data := make(Fields, len(entry.Data)+len(fields)) for k, v := range entry.Data { data[k] = v } From 636e0db9d70757e770a42663488f101120dcdcb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Radom=C3=ADr=20Sohlich?= Date: Wed, 16 Mar 2016 20:34:53 +0100 Subject: [PATCH 117/547] Hook for ElasticSearch. Added a link for ElasticSearch hook. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 49f7f07cb..57b232e96 100644 --- a/README.md +++ b/README.md @@ -225,6 +225,8 @@ 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) | | [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/) | +| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch| + #### Level logging From b2b4f8a4f42e2bf5ff32366629f5f0f8b9c14d10 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 17 Mar 2016 14:07:00 +0000 Subject: [PATCH 118/547] Rename LogrusLogger interface to FieldLogger --- logrus.go | 6 ++---- logrus_test.go | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/logrus.go b/logrus.go index 712199659..e59669111 100644 --- a/logrus.go +++ b/logrus.go @@ -108,10 +108,8 @@ type StdLogger interface { Panicln(...interface{}) } -// Logrus logger interface generalizes Entry and Logger types, so you can take any of these -type LogrusLogger interface { - // we can return LogrusLogger here, but this will require many changes and will - // possible break backward compatiblity +// The FieldLogger interface generalizes the Entry and Logger types +type FieldLogger interface { WithField(key string, value interface{}) *Entry WithFields(fields Fields) *Entry WithError(err error) *Entry diff --git a/logrus_test.go b/logrus_test.go index b4e9ded96..bfc478055 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -346,7 +346,7 @@ func TestLoggingRace(t *testing.T) { // Compile test func TestLogrusInterface(t *testing.T) { var buffer bytes.Buffer - fn := func(l LogrusLogger) { + fn := func(l FieldLogger) { b := l.WithField("key", "value") b.Debug("Test") } From 64042fe1f90aa2ac73b1688bd9ad4ec51caccd30 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 17 Mar 2016 14:11:10 +0000 Subject: [PATCH 119/547] changelog: update for 0.10.0 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e9e6009a..f2c2bc211 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 0.10.0 + +* feature: Add a test hook (#180) +* feature: `ParseLevel` is now case-insensitive (#326) +* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308) +* performance: avoid re-allocations on `WithFields` (#335) + # 0.9.0 * logrus/text_formatter: don't emit empty msg From 9e6b861e8d06e4007d0aeba1b6d786338a6c9701 Mon Sep 17 00:00:00 2001 From: "takuya.watabe" Date: Thu, 31 Mar 2016 00:20:06 +0900 Subject: [PATCH 120/547] Sumorus - SumoLogic Hook url added to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6e1721a74..59e1e1bf0 100644 --- a/README.md +++ b/README.md @@ -226,6 +226,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [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/) | | [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/)| #### Level logging From 0458d88d837ee138e04e247b2b32d5b3d054d5e4 Mon Sep 17 00:00:00 2001 From: Shintaro Kaneko Date: Sat, 2 Apr 2016 05:06:31 +0000 Subject: [PATCH 121/547] terminal: Include appengine tag to compile for GAE --- terminal_appengine.go | 8 ++++++++ terminal_bsd.go | 1 + terminal_linux.go | 2 ++ terminal_notwindows.go | 1 + terminal_solaris.go | 2 +- terminal_windows.go | 2 +- 6 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 terminal_appengine.go diff --git a/terminal_appengine.go b/terminal_appengine.go new file mode 100644 index 000000000..1960169ef --- /dev/null +++ b/terminal_appengine.go @@ -0,0 +1,8 @@ +// +build appengine + +package logrus + +// IsTerminal returns true if stderr's file descriptor is a terminal. +func IsTerminal() bool { + return true +} diff --git a/terminal_bsd.go b/terminal_bsd.go index 71f8d67a5..5f6be4d3c 100644 --- a/terminal_bsd.go +++ b/terminal_bsd.go @@ -1,4 +1,5 @@ // +build darwin freebsd openbsd netbsd dragonfly +// +build !appengine package logrus diff --git a/terminal_linux.go b/terminal_linux.go index a2c0b40db..308160ca8 100644 --- a/terminal_linux.go +++ b/terminal_linux.go @@ -3,6 +3,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !appengine + package logrus import "syscall" diff --git a/terminal_notwindows.go b/terminal_notwindows.go index b343b3a37..329038f6c 100644 --- a/terminal_notwindows.go +++ b/terminal_notwindows.go @@ -4,6 +4,7 @@ // license that can be found in the LICENSE file. // +build linux darwin freebsd openbsd netbsd dragonfly +// +build !appengine package logrus diff --git a/terminal_solaris.go b/terminal_solaris.go index 3e70bf7bf..a3c6f6e7d 100644 --- a/terminal_solaris.go +++ b/terminal_solaris.go @@ -1,4 +1,4 @@ -// +build solaris +// +build solaris,!appengine package logrus diff --git a/terminal_windows.go b/terminal_windows.go index 0146845d1..3727e8adf 100644 --- a/terminal_windows.go +++ b/terminal_windows.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 windows +// +build windows,!appengine package logrus From 430b39885592a2a93e92d592ad96c95b9584b4fa Mon Sep 17 00:00:00 2001 From: Vlad-Doru Ion Date: Mon, 4 Apr 2016 10:58:58 +0300 Subject: [PATCH 122/547] Added a new hook to the README.md Added the logrusus hook. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6e1721a74..874b21cd1 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [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 | From 169364d5b8335f4d79a90d5ca9d51ea4f18887f4 Mon Sep 17 00:00:00 2001 From: Max Lavrenov Date: Fri, 15 Apr 2016 18:22:24 +0300 Subject: [PATCH 123/547] fix race --- formatters/logstash/logstash.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/formatters/logstash/logstash.go b/formatters/logstash/logstash.go index aad646abf..2793af832 100644 --- a/formatters/logstash/logstash.go +++ b/formatters/logstash/logstash.go @@ -24,11 +24,13 @@ func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) { fields["@version"] = 1 - if f.TimestampFormat == "" { - f.TimestampFormat = logrus.DefaultTimestampFormat + timeStampFormat := f.TimestampFormat + + if timeStampFormat == "" { + timeStampFormat = logrus.DefaultTimestampFormat } - fields["@timestamp"] = entry.Time.Format(f.TimestampFormat) + fields["@timestamp"] = entry.Time.Format(timeStampFormat) // set message field v, ok := entry.Data["message"] From a380d1084ac30a3059eba8004608e0f79afd3e5f Mon Sep 17 00:00:00 2001 From: Antoine Grondin Date: Sat, 16 Apr 2016 12:42:53 +0530 Subject: [PATCH 124/547] add logstash hook to readme closes #339 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c43849ef1..6f04c8a19 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) | | [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/)| - +| [Logstash](https://github.com/bshuster-repo/logrus-logstash-hook) | Hook for logging to [Logstash](https://www.elastic.co/products/logstash) | #### Level logging From 72a1ee1c8125d5ad09f786269c35cfa95fe2d93d Mon Sep 17 00:00:00 2001 From: Damien Radtke Date: Tue, 1 Mar 2016 17:33:54 -0600 Subject: [PATCH 125/547] Add WriterLevel() function to the logger This commit adds a variant of the logger's Writer() function that accepts a log level. When the variant is used, any messages written to the returned pipe will be written with the provided level. The original Writer() function uses the logger's Print() method as it always has. --- writer.go | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/writer.go b/writer.go index 1e30b1c75..5768892df 100644 --- a/writer.go +++ b/writer.go @@ -7,18 +7,40 @@ import ( ) func (logger *Logger) Writer() *io.PipeWriter { + return logger.WriterLevel(255) +} + +func (logger *Logger) WriterLevel(level Level) *io.PipeWriter { reader, writer := io.Pipe() - go logger.writerScanner(reader) + var printFunc func(args ...interface{}) + switch level { + case DebugLevel: + printFunc = logger.Debug + case InfoLevel: + printFunc = logger.Info + case WarnLevel: + printFunc = logger.Warn + case ErrorLevel: + printFunc = logger.Error + case FatalLevel: + printFunc = logger.Fatal + case PanicLevel: + printFunc = logger.Panic + default: + printFunc = logger.Print + } + + go logger.writerScanner(reader, printFunc) runtime.SetFinalizer(writer, writerFinalizer) return writer } -func (logger *Logger) writerScanner(reader *io.PipeReader) { +func (logger *Logger) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) { scanner := bufio.NewScanner(reader) for scanner.Scan() { - logger.Print(scanner.Text()) + printFunc(scanner.Text()) } if err := scanner.Err(); err != nil { logger.Errorf("Error while reading from Writer: %s", err) From b83234a65ccf3a3c6733e865101af36edef5e1ef Mon Sep 17 00:00:00 2001 From: Antoine Grondin Date: Sat, 16 Apr 2016 12:46:11 +0530 Subject: [PATCH 126/547] use constant InfoLevel instead of 255 --- writer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/writer.go b/writer.go index 5768892df..f74d2aa5f 100644 --- a/writer.go +++ b/writer.go @@ -7,7 +7,7 @@ import ( ) func (logger *Logger) Writer() *io.PipeWriter { - return logger.WriterLevel(255) + return logger.WriterLevel(InfoLevel) } func (logger *Logger) WriterLevel(level Level) *io.PipeWriter { From ca8b641f7796c20dfb42a02013c79c85c97e15a7 Mon Sep 17 00:00:00 2001 From: Antoine Grondin Date: Sat, 16 Apr 2016 13:48:56 +0530 Subject: [PATCH 127/547] match name to semantic in `needsQuoting` fixes #196 --- text_formatter.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index 06ef20233..6afd0e031 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -128,10 +128,10 @@ func needsQuoting(text string) bool { (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '-' || ch == '.') { - return false + return true } } - return true + return false } func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { @@ -141,14 +141,14 @@ func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interf switch value := value.(type) { case string: - if needsQuoting(value) { + if !needsQuoting(value) { b.WriteString(value) } else { fmt.Fprintf(b, "%q", value) } case error: errmsg := value.Error() - if needsQuoting(errmsg) { + if !needsQuoting(errmsg) { b.WriteString(errmsg) } else { fmt.Fprintf(b, "%q", value) From dc646ed4d4e35e5c07c2778bb5bdf557ba6788ec Mon Sep 17 00:00:00 2001 From: Austin Riendeau Date: Mon, 18 Apr 2016 11:14:45 -0600 Subject: [PATCH 128/547] add go 1.6 to travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index ff23150dc..dee4eb2cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ go: - 1.3 - 1.4 - 1.5 + - 1.6 - tip install: - go get -t ./... From aea10af8b256e8c74ca5d05c0aa3bcb61af73f7e Mon Sep 17 00:00:00 2001 From: Tevin Zhang Date: Fri, 6 May 2016 11:48:51 +0800 Subject: [PATCH 129/547] Fix comment --- logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logger.go b/logger.go index 2fdb23176..09bbc9aa9 100644 --- a/logger.go +++ b/logger.go @@ -51,7 +51,7 @@ func New() *Logger { } } -// Adds a field to the log entry, note that you it doesn't log until you call +// Adds a field to the log entry, note that it doesn't log until you call // Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry. // If you want multiple fields, use `WithFields`. func (logger *Logger) WithField(key string, value interface{}) *Entry { From 575b95a4a6c75079643de18bf5b6c04f2fe9144d Mon Sep 17 00:00:00 2001 From: Guillaume Polaert Date: Tue, 17 May 2016 17:38:10 +0200 Subject: [PATCH 130/547] Add new hook for Logmatic.io Logmatic.io is Saas-based log management solution. We develop a simple hook in order to send your Logrus logs straight to Logmatic.io --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6f04c8a19..555fdced1 100644 --- a/README.md +++ b/README.md @@ -229,6 +229,8 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [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/)| | [Logstash](https://github.com/bshuster-repo/logrus-logstash-hook) | Hook for logging to [Logstash](https://www.elastic.co/products/logstash) | +| [Logmatic.io](https://github.com/logmatic/logmatic-go) | Hook for logging to [Logmatic.io](http://logmatic.io/) | + #### Level logging From cfbfb463dae59651a92723dc79c266fb740bd2dd Mon Sep 17 00:00:00 2001 From: Harry Moreno Date: Mon, 23 May 2016 19:54:24 -0400 Subject: [PATCH 131/547] change 'log' -> 'logrus' Change 'log' -> 'logrus' to fix syntax error --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6f04c8a19..5c6fc381c 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ import ( ) func main() { - log.WithFields(log.Fields{ + log.WithFields(logrus.Fields{ "animal": "walrus", }).Info("A walrus appears") } From 01b400f9b668d79dcd5f35707848aaa6dbd81ff4 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Wed, 1 Jun 2016 07:32:10 -0400 Subject: [PATCH 132/547] readme: fix example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c6fc381c..6f04c8a19 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ import ( ) func main() { - log.WithFields(logrus.Fields{ + log.WithFields(log.Fields{ "animal": "walrus", }).Info("A walrus appears") } From 81dc010f3f3fc9b8b05a3f33601a391e2f6a1358 Mon Sep 17 00:00:00 2001 From: Aaron Greenlee Date: Fri, 24 Jun 2016 10:23:56 -0400 Subject: [PATCH 133/547] Enhanced fatal calls so exit handlers can be invoked While GO offers the ability to recover from panic there is no way to intercept an os.Exit event. To allow graceful shutdown and clean-up or programs which use Logrus to Fatal out I've borrowed ideas from the `atexit` package and enhanced Logrus. Usage: * When setting up the logger one call `RegisterExitHandler( func() {...} )` to add a handler that will be invoked for any `Fatal` call to the logger. --- alt_exit.go | 60 ++++++++++++++++++++++++++++++++++++++ alt_exit_test.go | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ entry.go | 6 ++-- logger.go | 6 ++-- 4 files changed, 141 insertions(+), 6 deletions(-) create mode 100644 alt_exit.go create mode 100644 alt_exit_test.go diff --git a/alt_exit.go b/alt_exit.go new file mode 100644 index 000000000..6c354adc9 --- /dev/null +++ b/alt_exit.go @@ -0,0 +1,60 @@ +package logrus + +// The following code was sourced and modified from the +// https://bitbucket.org/tebeka/atexit package governed by the following license: +// +// Copyright (c) 2012 Miki Tebeka . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import ( +"fmt" +"os" +) + +var handlers = []func(){} + +func runHandler(handler func()) { +defer func() { +if err := recover(); err != nil { +fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err) +} +}() + +handler() +} + +func runHandlers() { +for _, handler := range handlers { +runHandler(handler) +} +} + +// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code) +func Exit(code int) { +runHandlers() +os.Exit(code) +} + +// RegisterExitHandler adds a Logrus atexit handler, call logrus.Exit to invoke +// all handlers. The handlers will also be invoked when any Fatal log entry is +// made. +func RegisterExitHandler(handler func()) { +handlers = append(handlers, handler) +} + diff --git a/alt_exit_test.go b/alt_exit_test.go new file mode 100644 index 000000000..69a3b5c72 --- /dev/null +++ b/alt_exit_test.go @@ -0,0 +1,75 @@ +package logrus + +import ( +"os/exec" +"testing" +"io/ioutil" +"time" +) + +func TestRegister(t *testing.T) { +current := len(handlers) +RegisterExitHandler(func() {}) +if len(handlers) != current+1 { +t.Fatalf("can't add handler") +} +} + +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") +} + +outfile := "/tmp/testprog.out" +arg := time.Now().UTC().String() +err := exec.Command("go", "run", gofile, outfile, arg).Run() +if err == nil { +t.Fatalf("completed normally, should have failed") +} + +data, err := ioutil.ReadFile(outfile) +if err != nil { +t.Fatalf("can't read output file %s", outfile) +} + +if string(data) != arg { +t.Fatalf("bad data") +} +} + +var testprog = []byte(` +// Test program for atexit, gets output file and data as arguments and writes +// data to output file in atexit handler. +package main + +import ( + "github.com/powerchordinc/logrus" + "flag" + "fmt" + "io/ioutil" +) + +var outfile = "" +var data = "" + +func handler() { + ioutil.WriteFile(outfile, []byte(data), 0666) +} + +func badHandler() { + n := 0 + fmt.Println(1/n) +} + +func main() { + flag.Parse() + outfile = flag.Arg(0) + data = flag.Arg(1) + + logrus.RegisterExitHandler(handler) + logrus.RegisterExitHandler(badHandler) + logrus.Fatal("Bye bye") +} +`) + diff --git a/entry.go b/entry.go index 89e966e7b..54bfc57d7 100644 --- a/entry.go +++ b/entry.go @@ -150,7 +150,7 @@ func (entry *Entry) Fatal(args ...interface{}) { if entry.Logger.Level >= FatalLevel { entry.log(FatalLevel, fmt.Sprint(args...)) } - os.Exit(1) + Exit(1) } func (entry *Entry) Panic(args ...interface{}) { @@ -198,7 +198,7 @@ func (entry *Entry) Fatalf(format string, args ...interface{}) { if entry.Logger.Level >= FatalLevel { entry.Fatal(fmt.Sprintf(format, args...)) } - os.Exit(1) + Exit(1) } func (entry *Entry) Panicf(format string, args ...interface{}) { @@ -245,7 +245,7 @@ func (entry *Entry) Fatalln(args ...interface{}) { if entry.Logger.Level >= FatalLevel { entry.Fatal(entry.sprintlnn(args...)) } - os.Exit(1) + Exit(1) } func (entry *Entry) Panicln(args ...interface{}) { diff --git a/logger.go b/logger.go index 2fdb23176..90c46e65a 100644 --- a/logger.go +++ b/logger.go @@ -108,7 +108,7 @@ func (logger *Logger) Fatalf(format string, args ...interface{}) { if logger.Level >= FatalLevel { NewEntry(logger).Fatalf(format, args...) } - os.Exit(1) + Exit(1) } func (logger *Logger) Panicf(format string, args ...interface{}) { @@ -155,7 +155,7 @@ func (logger *Logger) Fatal(args ...interface{}) { if logger.Level >= FatalLevel { NewEntry(logger).Fatal(args...) } - os.Exit(1) + Exit(1) } func (logger *Logger) Panic(args ...interface{}) { @@ -202,7 +202,7 @@ func (logger *Logger) Fatalln(args ...interface{}) { if logger.Level >= FatalLevel { NewEntry(logger).Fatalln(args...) } - os.Exit(1) + Exit(1) } func (logger *Logger) Panicln(args ...interface{}) { From 2fce953ebf245feff50e20191a63d2b4abcd4e3a Mon Sep 17 00:00:00 2001 From: Aaron Greenlee Date: Fri, 24 Jun 2016 10:24:56 -0400 Subject: [PATCH 134/547] Go fmt alt exit addition --- alt_exit.go | 29 +++++++++++++------------- alt_exit_test.go | 53 ++++++++++++++++++++++++------------------------ 2 files changed, 40 insertions(+), 42 deletions(-) diff --git a/alt_exit.go b/alt_exit.go index 6c354adc9..889a504c1 100644 --- a/alt_exit.go +++ b/alt_exit.go @@ -23,38 +23,37 @@ package logrus // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import ( -"fmt" -"os" + "fmt" + "os" ) var handlers = []func(){} func runHandler(handler func()) { -defer func() { -if err := recover(); err != nil { -fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err) -} -}() + defer func() { + if err := recover(); err != nil { + fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err) + } + }() -handler() + handler() } func runHandlers() { -for _, handler := range handlers { -runHandler(handler) -} + for _, handler := range handlers { + runHandler(handler) + } } // Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code) func Exit(code int) { -runHandlers() -os.Exit(code) + runHandlers() + os.Exit(code) } // RegisterExitHandler adds a Logrus atexit handler, call logrus.Exit to invoke // all handlers. The handlers will also be invoked when any Fatal log entry is // made. func RegisterExitHandler(handler func()) { -handlers = append(handlers, handler) + handlers = append(handlers, handler) } - diff --git a/alt_exit_test.go b/alt_exit_test.go index 69a3b5c72..28aa01cc0 100644 --- a/alt_exit_test.go +++ b/alt_exit_test.go @@ -1,41 +1,41 @@ package logrus import ( -"os/exec" -"testing" -"io/ioutil" -"time" + "io/ioutil" + "os/exec" + "testing" + "time" ) func TestRegister(t *testing.T) { -current := len(handlers) -RegisterExitHandler(func() {}) -if len(handlers) != current+1 { -t.Fatalf("can't add handler") -} + current := len(handlers) + RegisterExitHandler(func() {}) + if len(handlers) != current+1 { + t.Fatalf("can't add handler") + } } 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") -} + gofile := "/tmp/testprog.go" + if err := ioutil.WriteFile(gofile, testprog, 0666); err != nil { + t.Fatalf("can't create go file") + } -outfile := "/tmp/testprog.out" -arg := time.Now().UTC().String() -err := exec.Command("go", "run", gofile, outfile, arg).Run() -if err == nil { -t.Fatalf("completed normally, should have failed") -} + outfile := "/tmp/testprog.out" + arg := time.Now().UTC().String() + err := exec.Command("go", "run", gofile, outfile, arg).Run() + if err == nil { + t.Fatalf("completed normally, should have failed") + } -data, err := ioutil.ReadFile(outfile) -if err != nil { -t.Fatalf("can't read output file %s", outfile) -} + data, err := ioutil.ReadFile(outfile) + if err != nil { + t.Fatalf("can't read output file %s", outfile) + } -if string(data) != arg { -t.Fatalf("bad data") -} + if string(data) != arg { + t.Fatalf("bad data") + } } var testprog = []byte(` @@ -72,4 +72,3 @@ func main() { logrus.Fatal("Bye bye") } `) - From cb863525db3ccebdbccc0508792cefb8be637a25 Mon Sep 17 00:00:00 2001 From: Aaron Greenlee Date: Fri, 24 Jun 2016 10:49:02 -0400 Subject: [PATCH 135/547] Revised import path of logrus for pull-request CI --- alt_exit_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alt_exit_test.go b/alt_exit_test.go index 28aa01cc0..022b77830 100644 --- a/alt_exit_test.go +++ b/alt_exit_test.go @@ -44,7 +44,7 @@ var testprog = []byte(` package main import ( - "github.com/powerchordinc/logrus" + "github.com/Sirupsen/logrus" "flag" "fmt" "io/ioutil" From 467c9973c9f6f729f3eb51637999e4023790d1b5 Mon Sep 17 00:00:00 2001 From: Luke Petre Date: Tue, 12 Jul 2016 21:21:34 +0100 Subject: [PATCH 136/547] Removing logstash formatter --- README.md | 6 +-- formatters/logstash/logstash.go | 63 ---------------------------- formatters/logstash/logstash_test.go | 52 ----------------------- 3 files changed, 1 insertion(+), 120 deletions(-) delete mode 100644 formatters/logstash/logstash.go delete mode 100644 formatters/logstash/logstash_test.go diff --git a/README.md b/README.md index 6f04c8a19..ee8bd12be 100644 --- a/README.md +++ b/README.md @@ -306,14 +306,10 @@ The built-in logging formatters are: field to `true`. To force no colored output even if there is a TTY set the `DisableColors` field to `true` * `logrus.JSONFormatter`. Logs fields as JSON. -* `logrus/formatters/logstash.LogstashFormatter`. Logs fields as [Logstash](http://logstash.net) Events. - - ```go - logrus.SetFormatter(&logstash.LogstashFormatter{Type: "application_name"}) - ``` Third party logging formatters: +* [`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̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. diff --git a/formatters/logstash/logstash.go b/formatters/logstash/logstash.go deleted file mode 100644 index 2793af832..000000000 --- a/formatters/logstash/logstash.go +++ /dev/null @@ -1,63 +0,0 @@ -package logstash - -import ( - "encoding/json" - "fmt" - - "github.com/Sirupsen/logrus" -) - -// Formatter generates json in logstash format. -// Logstash site: http://logstash.net/ -type LogstashFormatter struct { - Type string // if not empty use for logstash type field. - - // TimestampFormat sets the format used for timestamps. - TimestampFormat string -} - -func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) { - fields := make(logrus.Fields) - for k, v := range entry.Data { - fields[k] = v - } - - fields["@version"] = 1 - - timeStampFormat := f.TimestampFormat - - if timeStampFormat == "" { - timeStampFormat = logrus.DefaultTimestampFormat - } - - fields["@timestamp"] = entry.Time.Format(timeStampFormat) - - // set message field - v, ok := entry.Data["message"] - if ok { - fields["fields.message"] = v - } - fields["message"] = entry.Message - - // set level field - v, ok = entry.Data["level"] - if ok { - fields["fields.level"] = v - } - fields["level"] = entry.Level.String() - - // set type field - if f.Type != "" { - v, ok = entry.Data["type"] - if ok { - fields["fields.type"] = v - } - fields["type"] = f.Type - } - - serialized, err := json.Marshal(fields) - if err != nil { - return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) - } - return append(serialized, '\n'), nil -} diff --git a/formatters/logstash/logstash_test.go b/formatters/logstash/logstash_test.go deleted file mode 100644 index d8814a0ea..000000000 --- a/formatters/logstash/logstash_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package logstash - -import ( - "bytes" - "encoding/json" - "github.com/Sirupsen/logrus" - "github.com/stretchr/testify/assert" - "testing" -) - -func TestLogstashFormatter(t *testing.T) { - assert := assert.New(t) - - lf := LogstashFormatter{Type: "abc"} - - fields := logrus.Fields{ - "message": "def", - "level": "ijk", - "type": "lmn", - "one": 1, - "pi": 3.14, - "bool": true, - } - - entry := logrus.WithFields(fields) - entry.Message = "msg" - entry.Level = logrus.InfoLevel - - b, _ := lf.Format(entry) - - var data map[string]interface{} - dec := json.NewDecoder(bytes.NewReader(b)) - dec.UseNumber() - dec.Decode(&data) - - // base fields - assert.Equal(json.Number("1"), data["@version"]) - assert.NotEmpty(data["@timestamp"]) - assert.Equal("abc", data["type"]) - assert.Equal("msg", data["message"]) - assert.Equal("info", data["level"]) - - // substituted fields - assert.Equal("def", data["fields.message"]) - assert.Equal("ijk", data["fields.level"]) - assert.Equal("lmn", data["fields.type"]) - - // formats - assert.Equal(json.Number("1"), data["one"]) - assert.Equal(json.Number("3.14"), data["pi"]) - assert.Equal(true, data["bool"]) -} From f62e104de320ad005e38eee175525dc7dfdcc39d Mon Sep 17 00:00:00 2001 From: Aaron Greenlee Date: Fri, 15 Jul 2016 22:56:31 -0400 Subject: [PATCH 137/547] Improved documentation of Fatal Handlers --- README.md | 16 ++++++++++++++++ alt_exit.go | 7 ++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ee8bd12be..f8302c373 100644 --- a/README.md +++ b/README.md @@ -384,3 +384,19 @@ assert.Equal("Hello error", hook.LastEntry().Message) hook.Reset() assert.Nil(hook.LastEntry()) ``` + +#### Fatal handlers + +Logrus can register one or more functions that will be called when any `fatal` +level message is logged. The registered handlers will be executed before +logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need +to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted. + +``` +... +handler := func() { + // gracefully shutdown something... +} +logrus.RegisterExitHandler(handler) +... +``` diff --git a/alt_exit.go b/alt_exit.go index 889a504c1..b4c9e8475 100644 --- a/alt_exit.go +++ b/alt_exit.go @@ -51,9 +51,14 @@ func Exit(code int) { os.Exit(code) } -// RegisterExitHandler adds a Logrus atexit handler, call logrus.Exit to invoke +// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke // all handlers. The handlers will also be invoked when any Fatal log entry is // made. +// +// This method is useful when a caller wishes to use logrus to log a fatal +// message but also needs to gracefully shutdown. An example usecase could be +// closing database connections, or sending a alert that the application is +// closing. func RegisterExitHandler(handler func()) { handlers = append(handlers, handler) } From fe509652aa9e3fd49089dd49d8d91a925be10227 Mon Sep 17 00:00:00 2001 From: Toby Date: Mon, 18 Jul 2016 16:36:46 +0100 Subject: [PATCH 138/547] Renaming 'Sirupsen' to 'sirupsen' --- doc.go | 4 ++-- examples/basic/basic.go | 2 +- examples/hook/hook.go | 5 +---- hooks/syslog/syslog.go | 3 ++- hooks/syslog/syslog_test.go | 3 ++- hooks/test/test.go | 2 +- hooks/test/test_test.go | 2 +- json_formatter.go | 2 +- 8 files changed, 11 insertions(+), 12 deletions(-) diff --git a/doc.go b/doc.go index dddd5f877..da67aba06 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 a1623ec00..b22468d9c 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" ) var log = logrus.New() diff --git a/examples/hook/hook.go b/examples/hook/hook.go index 3187f6d3e..892b2317f 100644 --- a/examples/hook/hook.go +++ b/examples/hook/hook.go @@ -1,9 +1,6 @@ package main -import ( - "github.com/Sirupsen/logrus" - "gopkg.in/gemnasium/logrus-airbrake-hook.v2" -) +import "github.com/sirupsen/logrus" var log = logrus.New() diff --git a/hooks/syslog/syslog.go b/hooks/syslog/syslog.go index a36e20032..fef980464 100644 --- a/hooks/syslog/syslog.go +++ b/hooks/syslog/syslog.go @@ -4,9 +4,10 @@ package logrus_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 42762dc10..89bd1ecb4 100644 --- a/hooks/syslog/syslog_test.go +++ b/hooks/syslog/syslog_test.go @@ -1,9 +1,10 @@ package logrus_syslog import ( - "github.com/Sirupsen/logrus" "log/syslog" "testing" + + "github.com/sirupsen/logrus" ) func TestLocalhostAddAndPrint(t *testing.T) { diff --git a/hooks/test/test.go b/hooks/test/test.go index 068812535..48c06ab23 100644 --- a/hooks/test/test.go +++ b/hooks/test/test.go @@ -3,7 +3,7 @@ package test import ( "io/ioutil" - "github.com/Sirupsen/logrus" + "github.com/sirupsen/logrus" ) // test.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 d69455ba0..3f55cfe31 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" ) diff --git a/json_formatter.go b/json_formatter.go index 2ad6dc5cf..47da50411 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -16,7 +16,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 25ee747bc7a7242d57160e55a2ba84a97ff3bd60 Mon Sep 17 00:00:00 2001 From: Toby Date: Wed, 27 Jul 2016 13:00:56 +0100 Subject: [PATCH 139/547] re-adding airbrake import --- examples/hook/hook.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/hook/hook.go b/examples/hook/hook.go index 892b2317f..04e08f42d 100644 --- a/examples/hook/hook.go +++ b/examples/hook/hook.go @@ -1,6 +1,9 @@ package main -import "github.com/sirupsen/logrus" +import ( + "github.com/sirupsen/logrus" + airbrake "gopkg.in/gemnasium/logrus-airbrake-hook.v2" +) var log = logrus.New() From 9b01a6992d52485df71c310898b76d7946529f77 Mon Sep 17 00:00:00 2001 From: Toby Date: Wed, 27 Jul 2016 13:12:26 +0100 Subject: [PATCH 140/547] Fixing import for airbrake and adding build constraint to example hook --- README.md | 18 +++++++++--------- alt_exit_test.go | 2 +- examples/hook/hook.go | 6 ++++++ hooks/syslog/README.md | 10 +++++----- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index f8302c373..8ce5db9ba 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) 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 @@ -54,7 +54,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() { @@ -65,7 +65,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: @@ -74,7 +74,7 @@ package main import ( "os" - log "github.com/Sirupsen/logrus" + log "github.com/sirupsen/logrus" ) func init() { @@ -123,7 +123,7 @@ application, you can also create an instance of the `logrus` Logger: package main import ( - "github.com/Sirupsen/logrus" + "github.com/sirupsen/logrus" ) // Create a new instance of the logger. You can have any number of instances. @@ -176,9 +176,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" ) @@ -203,7 +203,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. | | [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. | +| [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. | @@ -277,7 +277,7 @@ could do: ```go import ( - log "github.com/Sirupsen/logrus" + log "github.com/sirupsen/logrus" ) init() { diff --git a/alt_exit_test.go b/alt_exit_test.go index 022b77830..d18296348 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" diff --git a/examples/hook/hook.go b/examples/hook/hook.go index 04e08f42d..91b41eec5 100644 --- a/examples/hook/hook.go +++ b/examples/hook/hook.go @@ -1,3 +1,9 @@ +// +build ignore +// Do NOT include the above line in your code. This is a build contrainst used +// to prevent import loops in the code whilst go get'ting it. +// Read more about build contrains in golang here: +// https://golang.org/pkg/go/build/#hdr-Build_Constraints + package main import ( diff --git a/hooks/syslog/README.md b/hooks/syslog/README.md index 066704b37..92b391c17 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() { @@ -36,4 +36,4 @@ func main() { log.Hooks.Add(hook) } } -``` \ No newline at end of file +``` From bf6f3c550dee4e2218dc2d660e60bd52c5f19793 Mon Sep 17 00:00:00 2001 From: plan Date: Sat, 18 Jun 2016 21:47:44 +0800 Subject: [PATCH 141/547] Use Buffer pool to allocate bytes.Buffer for formatter Entry.Reader() seams not necessary, removed --- entry.go | 51 ++++++++++++++++++++++++++++------------------- text_formatter.go | 8 ++++++-- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/entry.go b/entry.go index 54bfc57d7..4edbe7a2d 100644 --- a/entry.go +++ b/entry.go @@ -3,11 +3,21 @@ package logrus import ( "bytes" "fmt" - "io" "os" + "sync" "time" ) +var bufferPool *sync.Pool + +func init() { + bufferPool = &sync.Pool{ + New: func() interface{} { + return new(bytes.Buffer) + }, + } +} + // Defines the key when adding errors using WithError. var ErrorKey = "error" @@ -29,6 +39,9 @@ type Entry struct { // Message passed to Debug, Info, Warn, Error, Fatal or Panic Message string + + // When formatter is called in entry.log(), an Buffer may be set to entry + Buffer *bytes.Buffer } func NewEntry(logger *Logger) *Entry { @@ -39,21 +52,15 @@ func NewEntry(logger *Logger) *Entry { } } -// Returns a reader for the entry, which is a proxy to the formatter. -func (entry *Entry) Reader() (*bytes.Buffer, error) { - serialized, err := entry.Logger.Formatter.Format(entry) - return bytes.NewBuffer(serialized), err -} - // Returns the string representation from the reader and ultimately the // formatter. func (entry *Entry) String() (string, error) { - reader, err := entry.Reader() + serialized, err := entry.Logger.Formatter.Format(entry) if err != nil { return "", err } - - return reader.String(), err + str := string(serialized) + return str, nil } // Add an error as single field (using the key defined in ErrorKey) to the Entry. @@ -81,6 +88,7 @@ func (entry *Entry) WithFields(fields Fields) *Entry { // This function is not declared with a pointer value because otherwise // race conditions will occur when using multiple goroutines func (entry Entry) log(level Level, msg string) { + var buffer *bytes.Buffer entry.Time = time.Now() entry.Level = level entry.Message = msg @@ -90,20 +98,23 @@ func (entry Entry) log(level Level, msg string) { fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) entry.Logger.mu.Unlock() } - - reader, err := entry.Reader() + buffer = bufferPool.Get().(*bytes.Buffer) + buffer.Reset() + defer bufferPool.Put(buffer) + entry.Buffer = buffer + serialized, err := entry.Logger.Formatter.Format(&entry) + entry.Buffer = nil if err != nil { entry.Logger.mu.Lock() fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) entry.Logger.mu.Unlock() - } - - entry.Logger.mu.Lock() - defer entry.Logger.mu.Unlock() - - _, err = io.Copy(entry.Logger.Out, reader) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) + } else { + entry.Logger.mu.Lock() + _, err = entry.Logger.Out.Write(serialized) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) + } + entry.Logger.mu.Unlock() } // To avoid Entry#log() returning a value that only would make sense for diff --git a/text_formatter.go b/text_formatter.go index 6afd0e031..cce61f2a0 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -57,6 +57,7 @@ type TextFormatter struct { } func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { + var b *bytes.Buffer var keys []string = make([]string, 0, len(entry.Data)) for k := range entry.Data { keys = append(keys, k) @@ -65,8 +66,11 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { if !f.DisableSorting { sort.Strings(keys) } - - b := &bytes.Buffer{} + if entry.Buffer != nil { + b = entry.Buffer + } else { + b = &bytes.Buffer{} + } prefixFieldClashes(entry.Data) From 88cefc67b0999f66ec0a82f886e844d6268ffbad Mon Sep 17 00:00:00 2001 From: plan Date: Sat, 18 Jun 2016 22:20:51 +0800 Subject: [PATCH 142/547] Add benchmark for logger --- logger_bench_test.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 logger_bench_test.go diff --git a/logger_bench_test.go b/logger_bench_test.go new file mode 100644 index 000000000..28a1a26b4 --- /dev/null +++ b/logger_bench_test.go @@ -0,0 +1,37 @@ +package logrus + +import ( + "os" + "testing" +) + +// smallFields is a small size data set for benchmarking +var loggerFields = Fields{ + "foo": "bar", + "baz": "qux", + "one": "two", + "three": "four", +} + +func BenchmarkDummyLogger(b *testing.B) { + nullf, err := os.OpenFile("/dev/null", os.O_WRONLY, 0666) + if err != nil { + b.Fatalf("%v", err) + } + defer nullf.Close() + doLoggerBenchmark(b, nullf, &TextFormatter{DisableColors: true}, smallFields) +} + +func doLoggerBenchmark(b *testing.B, out *os.File, formatter Formatter, fields Fields) { + logger := Logger{ + Out: out, + Level: InfoLevel, + Formatter: formatter, + } + entry := logger.WithFields(fields) + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + entry.Info("aaa") + } + }) +} From 69707b120c1ece5c39f4467501af6c783b4b007d Mon Sep 17 00:00:00 2001 From: plan Date: Sun, 19 Jun 2016 03:21:15 +0800 Subject: [PATCH 143/547] Provide logger.SetNoLock() to remove locking during log output Locking is enabled by default. When file is opened with appending mode, it's safe to write concurrently to a file. In this case user can choose to disable the lock. --- logger.go | 32 ++++++++++++++++++++++++++++++-- logger_bench_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/logger.go b/logger.go index 9052a8065..f3b64385f 100644 --- a/logger.go +++ b/logger.go @@ -26,8 +26,29 @@ type Logger struct { // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be // logged. `logrus.Debug` is useful in Level Level - // Used to sync writing to the log. - mu sync.Mutex + // Used to sync writing to the log. Locking is enabled by Default + mu MutexWrap +} + +type MutexWrap struct { + lock sync.Mutex + disabled bool +} + +func (mw *MutexWrap) Lock() { + if !mw.disabled { + mw.lock.Lock() + } +} + +func (mw *MutexWrap) Unlock() { + if !mw.disabled { + mw.lock.Unlock() + } +} + +func (mw *MutexWrap) Disable() { + mw.disabled = true } // Creates a new logger. Configuration should be set by changing `Formatter`, @@ -210,3 +231,10 @@ func (logger *Logger) Panicln(args ...interface{}) { NewEntry(logger).Panicln(args...) } } + +//When file is opened with appending mode, it's safe to +//write concurrently to a file (within 4k message on Linux). +//In these cases user can choose to disable the lock. +func (logger *Logger) SetNoLock() { + logger.mu.Disable() +} diff --git a/logger_bench_test.go b/logger_bench_test.go index 28a1a26b4..dd23a3535 100644 --- a/logger_bench_test.go +++ b/logger_bench_test.go @@ -22,6 +22,15 @@ func BenchmarkDummyLogger(b *testing.B) { doLoggerBenchmark(b, nullf, &TextFormatter{DisableColors: true}, smallFields) } +func BenchmarkDummyLoggerNoLock(b *testing.B) { + nullf, err := os.OpenFile("/dev/null", os.O_WRONLY|os.O_APPEND, 0666) + if err != nil { + b.Fatalf("%v", err) + } + defer nullf.Close() + doLoggerBenchmarkNoLock(b, nullf, &TextFormatter{DisableColors: true}, smallFields) +} + func doLoggerBenchmark(b *testing.B, out *os.File, formatter Formatter, fields Fields) { logger := Logger{ Out: out, @@ -35,3 +44,18 @@ func doLoggerBenchmark(b *testing.B, out *os.File, formatter Formatter, fields F } }) } + +func doLoggerBenchmarkNoLock(b *testing.B, out *os.File, formatter Formatter, fields Fields) { + logger := Logger{ + Out: out, + Level: InfoLevel, + Formatter: formatter, + } + logger.SetNoLock() + entry := logger.WithFields(fields) + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + entry.Info("aaa") + } + }) +} From 2df3d86da129c1682e1b20c8f647f56afd0b0e5b Mon Sep 17 00:00:00 2001 From: plan Date: Thu, 11 Aug 2016 01:39:36 +0800 Subject: [PATCH 144/547] Add document for logger.SetNoLock() --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index f8302c373..8ee3f965b 100644 --- a/README.md +++ b/README.md @@ -400,3 +400,20 @@ handler := func() { logrus.RegisterExitHandler(handler) ... ``` + +#### Thread safty + +By default Logger is protected by mutex for concurrent writes, this mutex is invoked when calling hooks and writing logs. +If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking. + +Situation when locking is not needed includes: + +* You have no hooks registered, or hooks calling is already thread-safe. + +* Writing to logger.Out is already thread-safe, for example: + + 1) logger.Out is protected by locks. + + 2) logger.Out is a os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allow multi-thread/multi-process writing) + + (Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/) From d19e13e7e8bb33cf54cf7dcd8aea011e8a2fb8c7 Mon Sep 17 00:00:00 2001 From: plan Date: Sun, 19 Jun 2016 04:08:25 +0800 Subject: [PATCH 145/547] Reuse entry from the same logger --- logger.go | 122 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 95 insertions(+), 27 deletions(-) diff --git a/logger.go b/logger.go index f3b64385f..b769f3d35 100644 --- a/logger.go +++ b/logger.go @@ -28,6 +28,8 @@ type Logger struct { Level Level // Used to sync writing to the log. Locking is enabled by Default mu MutexWrap + // Reusable empty entry + entryPool sync.Pool } type MutexWrap struct { @@ -72,163 +74,229 @@ func New() *Logger { } } +func (logger *Logger) newEntry() *Entry { + entry, ok := logger.entryPool.Get().(*Entry) + if ok { + return entry + } + return NewEntry(logger) +} + +func (logger *Logger) releaseEntry(entry *Entry) { + logger.entryPool.Put(entry) +} + // Adds a field to the log entry, note that it doesn't log until you call // Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry. // If you want multiple fields, use `WithFields`. func (logger *Logger) WithField(key string, value interface{}) *Entry { - return NewEntry(logger).WithField(key, value) + entry := logger.newEntry() + defer logger.releaseEntry(entry) + return entry.WithField(key, value) } // Adds a struct of fields to the log entry. All it does is call `WithField` for // each `Field`. func (logger *Logger) WithFields(fields Fields) *Entry { - return NewEntry(logger).WithFields(fields) + entry := logger.newEntry() + defer logger.releaseEntry(entry) + return entry.WithFields(fields) } // Add an error as single field to the log entry. All it does is call // `WithError` for the given `error`. func (logger *Logger) WithError(err error) *Entry { - return NewEntry(logger).WithError(err) + entry := logger.newEntry() + defer logger.releaseEntry(entry) + return entry.WithError(err) } func (logger *Logger) Debugf(format string, args ...interface{}) { if logger.Level >= DebugLevel { - NewEntry(logger).Debugf(format, args...) + entry := logger.newEntry() + entry.Debugf(format, args...) + logger.releaseEntry(entry) } } func (logger *Logger) Infof(format string, args ...interface{}) { if logger.Level >= InfoLevel { - NewEntry(logger).Infof(format, args...) + entry := logger.newEntry() + entry.Infof(format, args...) + logger.releaseEntry(entry) } } func (logger *Logger) Printf(format string, args ...interface{}) { - NewEntry(logger).Printf(format, args...) + entry := logger.newEntry() + entry.Printf(format, args...) + logger.releaseEntry(entry) } func (logger *Logger) Warnf(format string, args ...interface{}) { if logger.Level >= WarnLevel { - NewEntry(logger).Warnf(format, args...) + entry := logger.newEntry() + entry.Warnf(format, args...) + logger.releaseEntry(entry) } } func (logger *Logger) Warningf(format string, args ...interface{}) { if logger.Level >= WarnLevel { - NewEntry(logger).Warnf(format, args...) + entry := logger.newEntry() + entry.Warnf(format, args...) + logger.releaseEntry(entry) } } func (logger *Logger) Errorf(format string, args ...interface{}) { if logger.Level >= ErrorLevel { - NewEntry(logger).Errorf(format, args...) + entry := logger.newEntry() + entry.Errorf(format, args...) + logger.releaseEntry(entry) } } func (logger *Logger) Fatalf(format string, args ...interface{}) { if logger.Level >= FatalLevel { - NewEntry(logger).Fatalf(format, args...) + entry := logger.newEntry() + entry.Fatalf(format, args...) + logger.releaseEntry(entry) } Exit(1) } func (logger *Logger) Panicf(format string, args ...interface{}) { if logger.Level >= PanicLevel { - NewEntry(logger).Panicf(format, args...) + entry := logger.newEntry() + entry.Panicf(format, args...) + logger.releaseEntry(entry) } } func (logger *Logger) Debug(args ...interface{}) { if logger.Level >= DebugLevel { - NewEntry(logger).Debug(args...) + entry := logger.newEntry() + entry.Debug(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Info(args ...interface{}) { if logger.Level >= InfoLevel { - NewEntry(logger).Info(args...) + entry := logger.newEntry() + entry.Info(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Print(args ...interface{}) { - NewEntry(logger).Info(args...) + entry := logger.newEntry() + entry.Info(args...) + logger.releaseEntry(entry) } func (logger *Logger) Warn(args ...interface{}) { if logger.Level >= WarnLevel { - NewEntry(logger).Warn(args...) + entry := logger.newEntry() + entry.Warn(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Warning(args ...interface{}) { if logger.Level >= WarnLevel { - NewEntry(logger).Warn(args...) + entry := logger.newEntry() + entry.Warn(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Error(args ...interface{}) { if logger.Level >= ErrorLevel { - NewEntry(logger).Error(args...) + entry := logger.newEntry() + entry.Error(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Fatal(args ...interface{}) { if logger.Level >= FatalLevel { - NewEntry(logger).Fatal(args...) + entry := logger.newEntry() + entry.Fatal(args...) + logger.releaseEntry(entry) } Exit(1) } func (logger *Logger) Panic(args ...interface{}) { if logger.Level >= PanicLevel { - NewEntry(logger).Panic(args...) + entry := logger.newEntry() + entry.Panic(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Debugln(args ...interface{}) { if logger.Level >= DebugLevel { - NewEntry(logger).Debugln(args...) + entry := logger.newEntry() + entry.Debugln(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Infoln(args ...interface{}) { if logger.Level >= InfoLevel { - NewEntry(logger).Infoln(args...) + entry := logger.newEntry() + entry.Infoln(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Println(args ...interface{}) { - NewEntry(logger).Println(args...) + entry := logger.newEntry() + entry.Println(args...) + logger.releaseEntry(entry) } func (logger *Logger) Warnln(args ...interface{}) { if logger.Level >= WarnLevel { - NewEntry(logger).Warnln(args...) + entry := logger.newEntry() + entry.Warnln(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Warningln(args ...interface{}) { if logger.Level >= WarnLevel { - NewEntry(logger).Warnln(args...) + entry := logger.newEntry() + entry.Warnln(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Errorln(args ...interface{}) { if logger.Level >= ErrorLevel { - NewEntry(logger).Errorln(args...) + entry := logger.newEntry() + entry.Errorln(args...) + logger.releaseEntry(entry) } } func (logger *Logger) Fatalln(args ...interface{}) { if logger.Level >= FatalLevel { - NewEntry(logger).Fatalln(args...) + entry := logger.newEntry() + entry.Fatalln(args...) + logger.releaseEntry(entry) } Exit(1) } func (logger *Logger) Panicln(args ...interface{}) { if logger.Level >= PanicLevel { - NewEntry(logger).Panicln(args...) + entry := logger.newEntry() + entry.Panicln(args...) + logger.releaseEntry(entry) } } From fef8a196c7e1b85ceff5565e6dba18496210cc89 Mon Sep 17 00:00:00 2001 From: Toby Date: Tue, 16 Aug 2016 17:04:58 +0100 Subject: [PATCH 146/547] Correcting typos --- examples/hook/hook.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/hook/hook.go b/examples/hook/hook.go index 91b41eec5..707a5bbf4 100644 --- a/examples/hook/hook.go +++ b/examples/hook/hook.go @@ -1,7 +1,7 @@ // +build ignore -// Do NOT include the above line in your code. This is a build contrainst used +// Do NOT include the above line in your code. This is a build constraint used // to prevent import loops in the code whilst go get'ting it. -// Read more about build contrains in golang here: +// Read more about build constraints in golang here: // https://golang.org/pkg/go/build/#hdr-Build_Constraints package main From 69047d56ce23bcb625b4caa7554f331ac6c5c30b Mon Sep 17 00:00:00 2001 From: Peter Kieltyka Date: Tue, 23 Aug 2016 13:23:35 -0400 Subject: [PATCH 147/547] 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 cce61f2a0..cdd4f3116 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 ba9f5caf7413a24c27cd4555cf5819ee5e72c648 Mon Sep 17 00:00:00 2001 From: Carolyn Van Slyck Date: Wed, 24 Aug 2016 16:59:41 -0500 Subject: [PATCH 148/547] Allow disabling timestamps with colored output --- text_formatter.go | 4 +++- text_formatter_test.go | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/text_formatter.go b/text_formatter.go index cce61f2a0..b28247029 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -115,7 +115,9 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin levelText := strings.ToUpper(entry.Level.String())[0:4] - if !f.FullTimestamp { + if f.DisableTimestamp { + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s ", levelColor, levelText, entry.Message) + } else if !f.FullTimestamp { fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) } else { fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message) diff --git a/text_formatter_test.go b/text_formatter_test.go index e25a44f67..107703fa6 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -5,6 +5,7 @@ import ( "errors" "testing" "time" + "strings" ) func TestQuoting(t *testing.T) { @@ -57,5 +58,14 @@ func TestTimestampFormat(t *testing.T) { checkTimeStr("") } +func TestDisableTimestampWithColoredOutput(t *testing.T) { + tf := &TextFormatter{DisableTimestamp: true, ForceColors: true} + + b, _ := tf.Format(WithField("test", "test")) + if strings.Contains(string(b), "[0000]") { + t.Error("timestamp not expected when DisableTimestamp is true") + } +} + // TODO add tests for sorting etc., this requires a parser for the text // formatter output. From e41b67afee9a38cbe4c7e66ab759723494ab1b4a Mon Sep 17 00:00:00 2001 From: Tony Lee Date: Wed, 31 Aug 2016 23:54:59 +1000 Subject: [PATCH 149/547] Added ability to disable level text truncation. Fixes #406 --- text_formatter.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/text_formatter.go b/text_formatter.go index cce61f2a0..a51bf1973 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -54,6 +54,9 @@ type TextFormatter struct { // that log extremely frequently and don't use the JSON formatter this may not // be desired. DisableSorting bool + + // Disables the truncation of the level text to 4 characters. + DisableLevelTruncation bool } func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { @@ -113,7 +116,10 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin levelColor = blue } - levelText := strings.ToUpper(entry.Level.String())[0:4] + levelText := strings.ToUpper(entry.Level.String()) + if !f.DisableLevelTruncation { + levelText = levelText[0:4] + } if !f.FullTimestamp { fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) From 7bc22eed88902b5ee33b9cc870b4af8fb748c7fb Mon Sep 17 00:00:00 2001 From: Tony Lee Date: Thu, 1 Sep 2016 00:28:23 +1000 Subject: [PATCH 150/547] Added testing for DisableLevelTruncation --- text_formatter_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/text_formatter_test.go b/text_formatter_test.go index e25a44f67..6b5353dec 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -3,6 +3,7 @@ package logrus import ( "bytes" "errors" + "strings" "testing" "time" ) @@ -57,5 +58,43 @@ func TestTimestampFormat(t *testing.T) { checkTimeStr("") } +func TestDisableLevelTruncation(t *testing.T) { + entry := &Entry{ + Time: time.Now(), + Message: "testing", + } + keys := []string{} + timestampFormat := "Mon Jan 2 15:04:05 -0700 MST 2006" + checkDisableTruncation := func(disabled bool, level Level) { + tf := &TextFormatter{DisableLevelTruncation: disabled} + var b bytes.Buffer + entry.Level = level + tf.printColored(&b, entry, keys, timestampFormat) + logLine := (&b).String() + if disabled { + expected := strings.ToUpper(level.String()) + if !strings.Contains(logLine, expected) { + t.Errorf("level string expected to be %s when truncation disabled", expected) + } + } else { + expected := strings.ToUpper(level.String()) + if len(level.String()) > 4 { + if strings.Contains(logLine, expected) { + t.Errorf("level string %s expected to be truncated to %s when truncation is enabled", expected, expected[0:4]) + } + } else { + if !strings.Contains(logLine, expected) { + t.Errorf("level string expected to be %s when truncation is enabled and level string is below truncation threshold", expected) + } + } + } + } + + checkDisableTruncation(true, DebugLevel) + checkDisableTruncation(true, InfoLevel) + checkDisableTruncation(false, ErrorLevel) + checkDisableTruncation(false, InfoLevel) +} + // TODO add tests for sorting etc., this requires a parser for the text // formatter output. From 7f36b88c5a273161f259c366babbde25c39cbc98 Mon Sep 17 00:00:00 2001 From: Sagar Sadashiv Patwardhan Date: Sun, 18 Sep 2016 12:42:16 -0700 Subject: [PATCH 151/547] Add scribe hook --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ab48929df..8255e544e 100644 --- a/README.md +++ b/README.md @@ -228,6 +228,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) | | [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) | | [Logmatic.io](https://github.com/logmatic/logmatic-go) | Hook for logging to [Logmatic.io](http://logmatic.io/) | From e64474aff6fc62218a2adc090f3464f0154e342d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Depierrepont=20aka=20Toorop?= Date: Mon, 19 Sep 2016 16:09:08 +0200 Subject: [PATCH 152/547] Add Pushover Hook --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ab48929df..52b8325c7 100644 --- a/README.md +++ b/README.md @@ -230,6 +230,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/)| | [Logstash](https://github.com/bshuster-repo/logrus-logstash-hook) | Hook for logging to [Logstash](https://www.elastic.co/products/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) | #### Level logging From 0a0dcbf3a3ecb0d62d2c3491a47f33aca367952e Mon Sep 17 00:00:00 2001 From: Aaron Lehmann Date: Wed, 28 Sep 2016 11:55:00 +0100 Subject: [PATCH 153/547] Fix formatting of wrapped errors when colors are used There are two different code paths for rendering a key/value pair. The non-color version uses a type switch that handles specific types such as "error", and the color version uses the %+v printf format specifier. This causes an inconsistency between the two formats. In particular, errors created using the github.com/pkg/errors package will include a stack trace of where the error was created when printed to the terminal, but not to a file. Printing the stack trace as part of the log field is probably not the right behavior. The output is also inconsistent between the two forms because strings are not quoted/escaped when colors are used. This can make log output unparseable. Fix this by making both code paths use the type switch and escaping rules. Fix the escaping code to pass the error value to Fprintf, not the error itself, which seems to be necessary to avoid blank output with errors created by github.com/pkg/errors. --- text_formatter.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index cce61f2a0..9114b3ca4 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -122,7 +122,8 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin } for _, k := range keys { v := entry.Data[k] - fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v) + fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k) + f.appendValue(b, v) } } @@ -142,7 +143,11 @@ func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interf b.WriteString(key) b.WriteByte('=') + f.appendValue(b, value) + b.WriteByte(' ') +} +func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { switch value := value.(type) { case string: if !needsQuoting(value) { @@ -155,11 +160,9 @@ func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interf if !needsQuoting(errmsg) { b.WriteString(errmsg) } else { - fmt.Fprintf(b, "%q", value) + fmt.Fprintf(b, "%q", errmsg) } default: fmt.Fprint(b, value) } - - b.WriteByte(' ') } From f033a23c716bf8ed4ad75227f52e28d4ceb9e4ee Mon Sep 17 00:00:00 2001 From: Alexandre Richonnier Date: Wed, 5 Oct 2016 09:20:26 +0200 Subject: [PATCH 154/547] Update README.md add link to Logrus and Viper integration Helper --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ab48929df..527eef13c 100644 --- a/README.md +++ b/README.md @@ -367,6 +367,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) | #### Testing From 0bbd8db86621c5c14f87c74046d8856e090c4ea2 Mon Sep 17 00:00:00 2001 From: JJ Merelo Date: Sun, 9 Oct 2016 13:02:24 +0200 Subject: [PATCH 155/547] Adds new logz.io hook by @ripcurld00d --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ab48929df..ae61df242 100644 --- a/README.md +++ b/README.md @@ -229,6 +229,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [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/)| | [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/) | From 141d6e90cc9ed3191a03fecdfb8cdc6d49d4771d Mon Sep 17 00:00:00 2001 From: irfan sharif Date: Thu, 3 Nov 2016 01:28:25 -0400 Subject: [PATCH 156/547] Updated .travis.yml to ignore /examples/ /examples/hook/hook.go with it's child dependency on airbrake/gobrake is not backwards compatible pre-go1.6 due to use of the following: - os.LookupEnv (introduced in go1.5) - http.StatusTooManyRequests (introduced in go1.6) ignoring the fetch and explicit test of /examples/ fixes failing go1.3, go1.4, go1.5 builds. --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index dee4eb2cc..abd0c9c94 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,9 @@ go: - 1.4 - 1.5 - 1.6 + - 1.7 - tip install: - - go get -t ./... -script: GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v ./... + - go get -t $(go list ./... | grep -v /examples/) +script: + - GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v $(go list ./... | grep -v /examples/) From f57026fd2ff35fdc0d181bd3e783bb9d457ea0f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Lafoucrie=CC=80re?= Date: Thu, 3 Nov 2016 21:59:20 -0400 Subject: [PATCH 157/547] Add pglogrus to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 126cd1fc2..f9cfb0a5d 100644 --- a/README.md +++ b/README.md @@ -233,6 +233,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [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) | #### Level logging From 4432d8c2250e56a31d5f219af01bd4296b5ab28e Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Fri, 11 Nov 2016 07:09:38 -0500 Subject: [PATCH 158/547] Remove legacy go versions --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index abd0c9c94..ec7dd7833 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,5 @@ language: go go: - - 1.3 - - 1.4 - - 1.5 - 1.6 - 1.7 - tip From eff43b1166387f3dc7ee0b8f5fa174f4ef2c812f Mon Sep 17 00:00:00 2001 From: Ryan Canty Date: Mon, 15 Aug 2016 11:04:25 -0700 Subject: [PATCH 159/547] Added customizable keys to JSON formatter --- json_formatter.go | 24 +++++++++++++++++++++--- json_formatter_test.go | 41 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/json_formatter.go b/json_formatter.go index 2ad6dc5cf..7364fe890 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -8,6 +8,9 @@ import ( type JSONFormatter struct { // TimestampFormat sets the format used for marshaling timestamps. TimestampFormat string + MessageKey string + LevelKey string + TimeKey string } func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { @@ -29,9 +32,24 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { timestampFormat = DefaultTimestampFormat } - data["time"] = entry.Time.Format(timestampFormat) - data["msg"] = entry.Message - data["level"] = entry.Level.String() + timeKey := f.TimeKey + if timeKey == "" { + timeKey = "time" + } + + messageKey := f.MessageKey + if messageKey == "" { + messageKey = "msg" + } + + levelKey := f.LevelKey + if levelKey == "" { + levelKey = "level" + } + + data[timeKey] = entry.Time.Format(timestampFormat) + data[messageKey] = entry.Message + data[levelKey] = entry.Level.String() serialized, err := json.Marshal(data) if err != nil { diff --git a/json_formatter_test.go b/json_formatter_test.go index 1d7087325..0fa346633 100644 --- a/json_formatter_test.go +++ b/json_formatter_test.go @@ -3,7 +3,7 @@ package logrus import ( "encoding/json" "errors" - + "strings" "testing" ) @@ -118,3 +118,42 @@ func TestJSONEntryEndsWithNewline(t *testing.T) { t.Fatal("Expected JSON log entry to end with a newline") } } + +func TestJSONMessageKey(t *testing.T) { + formatter := &JSONFormatter{MessageKey: "message"} + + b, err := formatter.Format(&Entry{Message: "oh hai"}) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + s := string(b) + if !(strings.Contains(s, "message") && strings.Contains(s, "oh hai")) { + t.Fatal("Expected JSON to format message key") + } +} + +func TestJSONLevelKey(t *testing.T) { + formatter := &JSONFormatter{LevelKey: "somelevel"} + + b, err := formatter.Format(WithField("level", "something")) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + s := string(b) + if !strings.Contains(s, "somelevel") { + t.Fatal("Expected JSON to format level key") + } +} + +func TestJSONTimeKey(t *testing.T) { + formatter := &JSONFormatter{TimeKey: "timeywimey"} + + b, err := formatter.Format(WithField("level", "something")) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + s := string(b) + if !strings.Contains(s, "timeywimey") { + t.Fatal("Expected JSON to format time key") + } +} From 92e355c5e3fe1e6cfe35bc94de149b83defd515d Mon Sep 17 00:00:00 2001 From: Ryan Canty Date: Thu, 17 Nov 2016 11:28:41 -0800 Subject: [PATCH 160/547] Added resolve method to clean up Format --- json_formatter.go | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/json_formatter.go b/json_formatter.go index 7364fe890..0c74e5af7 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -5,6 +5,14 @@ import ( "fmt" ) +type fieldKey string + +const ( + DefaultKeyMsg = "msg" + DefaultKeyLevel = "level" + DefaultKeyTime = "time" +) + type JSONFormatter struct { // TimestampFormat sets the format used for marshaling timestamps. TimestampFormat string @@ -32,24 +40,9 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { timestampFormat = DefaultTimestampFormat } - timeKey := f.TimeKey - if timeKey == "" { - timeKey = "time" - } - - messageKey := f.MessageKey - if messageKey == "" { - messageKey = "msg" - } - - levelKey := f.LevelKey - if levelKey == "" { - levelKey = "level" - } - - data[timeKey] = entry.Time.Format(timestampFormat) - data[messageKey] = entry.Message - data[levelKey] = entry.Level.String() + data[f.resolveKey(f.TimeKey, DefaultKeyTime)] = entry.Time.Format(timestampFormat) + data[f.resolveKey(f.MessageKey, DefaultKeyMsg)] = entry.Message + data[f.resolveKey(f.LevelKey, DefaultKeyLevel)] = entry.Level.String() serialized, err := json.Marshal(data) if err != nil { @@ -57,3 +50,10 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { } return append(serialized, '\n'), nil } + +func (f *JSONFormatter) resolveKey(key, defaultKey string) string { + if len(key) > 0 { + return key + } + return defaultKey +} From 0bb9ed55311d2ea4c3b72788cde3384d76df7377 Mon Sep 17 00:00:00 2001 From: Ryan Canty Date: Thu, 17 Nov 2016 15:16:46 -0800 Subject: [PATCH 161/547] Added FieldMap to reduce potential struct bloat --- json_formatter.go | 33 ++++++++++++++++----------------- json_formatter_test.go | 18 +++++++++++++++--- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/json_formatter.go b/json_formatter.go index 0c74e5af7..1e3e2bc86 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -4,21 +4,27 @@ import ( "encoding/json" "fmt" ) - type fieldKey string +type FieldMap map[fieldKey]string const ( - DefaultKeyMsg = "msg" - DefaultKeyLevel = "level" - DefaultKeyTime = "time" + FieldKeyMsg = "msg" + FieldKeyLevel = "level" + FieldKeyTime = "time" ) +func (f FieldMap) resolve(key fieldKey) string { + if k, ok := f[key]; ok { + return k + } + + return string(key) +} + type JSONFormatter struct { // TimestampFormat sets the format used for marshaling timestamps. TimestampFormat string - MessageKey string - LevelKey string - TimeKey string + FieldMap FieldMap } func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { @@ -40,9 +46,9 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { timestampFormat = DefaultTimestampFormat } - data[f.resolveKey(f.TimeKey, DefaultKeyTime)] = entry.Time.Format(timestampFormat) - data[f.resolveKey(f.MessageKey, DefaultKeyMsg)] = entry.Message - data[f.resolveKey(f.LevelKey, DefaultKeyLevel)] = entry.Level.String() + data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat) + data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message + data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() serialized, err := json.Marshal(data) if err != nil { @@ -50,10 +56,3 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { } return append(serialized, '\n'), nil } - -func (f *JSONFormatter) resolveKey(key, defaultKey string) string { - if len(key) > 0 { - return key - } - return defaultKey -} diff --git a/json_formatter_test.go b/json_formatter_test.go index 0fa346633..5baa93e4b 100644 --- a/json_formatter_test.go +++ b/json_formatter_test.go @@ -120,7 +120,11 @@ func TestJSONEntryEndsWithNewline(t *testing.T) { } func TestJSONMessageKey(t *testing.T) { - formatter := &JSONFormatter{MessageKey: "message"} + formatter := &JSONFormatter{ + FieldMap: FieldMap{ + FieldKeyMsg: "message", + }, + } b, err := formatter.Format(&Entry{Message: "oh hai"}) if err != nil { @@ -133,7 +137,11 @@ func TestJSONMessageKey(t *testing.T) { } func TestJSONLevelKey(t *testing.T) { - formatter := &JSONFormatter{LevelKey: "somelevel"} + formatter := &JSONFormatter{ + FieldMap: FieldMap{ + FieldKeyLevel: "somelevel", + }, + } b, err := formatter.Format(WithField("level", "something")) if err != nil { @@ -146,7 +154,11 @@ func TestJSONLevelKey(t *testing.T) { } func TestJSONTimeKey(t *testing.T) { - formatter := &JSONFormatter{TimeKey: "timeywimey"} + formatter := &JSONFormatter{ + FieldMap: FieldMap{ + FieldKeyTime: "timeywimey", + }, + } b, err := formatter.Format(WithField("level", "something")) if err != nil { From f486969ad6ebb3e3b17c5570703d8033b820f705 Mon Sep 17 00:00:00 2001 From: Ryan Canty Date: Fri, 18 Nov 2016 11:02:11 -0800 Subject: [PATCH 162/547] Added comment documentation for FieldMap --- json_formatter.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/json_formatter.go b/json_formatter.go index 1e3e2bc86..f3729bff7 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" ) + type fieldKey string type FieldMap map[fieldKey]string @@ -24,7 +25,17 @@ func (f FieldMap) resolve(key fieldKey) string { type JSONFormatter struct { // TimestampFormat sets the format used for marshaling timestamps. TimestampFormat string - FieldMap FieldMap + + // FieldMap allows users to customize the names of keys for various fields. + // As an example: + // formatter := &JSONFormatter{ + // FieldMap: FieldMap{ + // FieldKeyTime: "@timestamp", + // FieldKeyLevel: "@level", + // FieldKeyLevel: "@message", + // }, + // } + FieldMap FieldMap } func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { From f4a02192881a8fe675065211b7da6c2f10942b23 Mon Sep 17 00:00:00 2001 From: Andrew Burian Date: Mon, 21 Nov 2016 10:09:59 -0800 Subject: [PATCH 163/547] Added option to disable JSON timestamp Tests verify both the default and disabled case. --- json_formatter.go | 7 ++++++- json_formatter_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/json_formatter.go b/json_formatter.go index f3729bff7..266554e9f 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -26,6 +26,9 @@ type JSONFormatter struct { // TimestampFormat sets the format used for marshaling timestamps. TimestampFormat string + // DisableTimestamp allows disabling automatic timestamps in output + DisableTimestamp bool + // FieldMap allows users to customize the names of keys for various fields. // As an example: // formatter := &JSONFormatter{ @@ -57,7 +60,9 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { timestampFormat = DefaultTimestampFormat } - data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat) + if !f.DisableTimestamp { + data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat) + } data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() diff --git a/json_formatter_test.go b/json_formatter_test.go index 5baa93e4b..2f9dae700 100644 --- a/json_formatter_test.go +++ b/json_formatter_test.go @@ -169,3 +169,31 @@ func TestJSONTimeKey(t *testing.T) { t.Fatal("Expected JSON to format time key") } } + +func TestJSONDisableTimestamp(t *testing.T) { + formatter := &JSONFormatter{ + DisableTimestamp: true, + } + + b, err := formatter.Format(WithField("level", "something")) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + s := string(b) + if strings.Contains(s, "time") { + t.Error("Did not prevent timestamp", s) + } +} + +func TestJSONEnableTimestamp(t *testing.T) { + formatter := &JSONFormatter{} + + b, err := formatter.Format(WithField("level", "something")) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + s := string(b) + if !strings.Contains(s, "time") { + t.Error("Timestamp not present", s) + } +} From 41f32844d88a12b819d35c63b419590f8393a953 Mon Sep 17 00:00:00 2001 From: Andrew Burian Date: Mon, 21 Nov 2016 10:16:24 -0800 Subject: [PATCH 164/547] Switched hardcoded string for const value --- json_formatter_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/json_formatter_test.go b/json_formatter_test.go index 2f9dae700..51093a79b 100644 --- a/json_formatter_test.go +++ b/json_formatter_test.go @@ -180,7 +180,7 @@ func TestJSONDisableTimestamp(t *testing.T) { t.Fatal("Unable to format entry: ", err) } s := string(b) - if strings.Contains(s, "time") { + if strings.Contains(s, FieldKeyTime) { t.Error("Did not prevent timestamp", s) } } @@ -193,7 +193,7 @@ func TestJSONEnableTimestamp(t *testing.T) { t.Fatal("Unable to format entry: ", err) } s := string(b) - if !strings.Contains(s, "time") { + if !strings.Contains(s, FieldKeyTime) { t.Error("Timestamp not present", s) } } From b79496d0febf58eef021e806522d632eef450236 Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Wed, 23 Nov 2016 16:19:58 -0800 Subject: [PATCH 165/547] Revert "Updated .travis.yml to ignore /examples/" This reverts commit 141d6e90cc9ed3191a03fecdfb8cdc6d49d4771d. --- .travis.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index ec7dd7833..efed8ea58 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,7 @@ language: go go: - 1.6 - - 1.7 - tip install: - - go get -t $(go list ./... | grep -v /examples/) -script: - - GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v $(go list ./... | grep -v /examples/) + - go get -t ./... +script: GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v ./... From c1880e89ed6bf596f023046b7cd0746d79a4c6d7 Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Wed, 23 Nov 2016 16:20:51 -0800 Subject: [PATCH 166/547] travis: build Go 1.7 as well, removed by revert Signed-off-by: Stephen J Day --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index efed8ea58..804c56943 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: go go: - 1.6 + - 1.7 - tip install: - go get -t ./... From eef30191f02f59179ebe4be206bc7f62b839039b Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Fri, 25 Nov 2016 19:02:56 -0800 Subject: [PATCH 167/547] First cut at adding calling method If log.SetReportMethod(true) then method=PACKAGE.FUNCTION will be added as a field to log lines. eg: time="2016-11-25T19:04:43-08:00" level=info method=main msg="log testing" TODO: documentation, examples --- README.md | 7 ++++ alt_exit_test.go | 20 ++++++++++-- entry.go | 47 +++++++++++++++++++++++++-- exported.go | 16 +++++++++ formatter.go | 10 +++++- json_formatter.go | 20 +++++++----- json_formatter_test.go | 48 +++++++++++++++++++++++++++ logger.go | 13 +++++--- logrus_test.go | 73 ++++++++++++++++++++++++++++++++++++++++++ text_formatter.go | 14 ++++++-- 10 files changed, 248 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index f9cfb0a5d..275c1131a 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,13 @@ time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca s time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true exit status 1 ``` +To ensure this behaviour even if a TTY is attached, set your formatter as follows: +``` + log.SetFormatter(&log.TextFormatter{ + DisableColors: true, + FullTimestamp: true, + }) +``` #### Example diff --git a/alt_exit_test.go b/alt_exit_test.go index 022b77830..5763c60e3 100644 --- a/alt_exit_test.go +++ b/alt_exit_test.go @@ -3,6 +3,8 @@ package logrus import ( "io/ioutil" "os/exec" + "runtime" + "strings" "testing" "time" ) @@ -17,6 +19,9 @@ func TestRegister(t *testing.T) { func TestHandler(t *testing.T) { gofile := "/tmp/testprog.go" + testprog := testprogleader + testprog = append(testprog, getPackage()...) + testprog = append(testprog, testprogtrailer...) if err := ioutil.WriteFile(gofile, testprog, 0666); err != nil { t.Fatalf("can't create go file") } @@ -38,13 +43,24 @@ func TestHandler(t *testing.T) { } } -var testprog = []byte(` +// getPackage returns the name of the current package, which makes running this +// test in a fork simpler +func getPackage() []byte { + pc, _, _, _ := runtime.Caller(0) + fullFuncName := runtime.FuncForPC(pc).Name() + idx := strings.LastIndex(fullFuncName, ".") + return []byte(fullFuncName[:idx]) // trim off function details +} + +var testprogleader = []byte(` // Test program for atexit, gets output file and data as arguments and writes // data to output file in atexit handler. package main import ( - "github.com/Sirupsen/logrus" + "`) +var testprogtrailer = []byte( + `" "flag" "fmt" "io/ioutil" diff --git a/entry.go b/entry.go index 4edbe7a2d..1e721cefe 100644 --- a/entry.go +++ b/entry.go @@ -4,6 +4,9 @@ import ( "bytes" "fmt" "os" + "regexp" + "runtime" + "strings" "sync" "time" ) @@ -37,6 +40,9 @@ type Entry struct { // Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic Level Level + // Calling method, with package name + Method string + // Message passed to Debug, Info, Warn, Error, Fatal or Panic Message string @@ -47,8 +53,8 @@ type Entry struct { func NewEntry(logger *Logger) *Entry { return &Entry{ Logger: logger, - // Default is three fields, give a little extra room - Data: make(Fields, 5), + // Default is three fields, plus one optional. Give a little extra room. + Data: make(Fields, 6), } } @@ -85,6 +91,39 @@ func (entry *Entry) WithFields(fields Fields) *Entry { return &Entry{Logger: entry.Logger, Data: data} } +// getCaller retrieves the name of the first non-logrus calling function +func getCaller() (method string) { + // Restrict the lookback to 25 frames - if it's further than that, report UNKNOWN + pcs := make([]uintptr, 25) + + // the first non-logrus caller is at least three frames away + depth := runtime.Callers(3, pcs) + for i := 0; i < depth; i++ { + fullFuncName := runtime.FuncForPC(pcs[i]).Name() + idx := strings.LastIndex(fullFuncName, "/") + 1 + if idx > 0 { + fullFuncName = fullFuncName[idx:] + } + + matched, err := regexp.MatchString("logrus.*", fullFuncName) + if err != nil { + return "CALLER_LOOKUP_FAILED" + } + + // If the caller isn't part of logrus, we're done + if !matched { + if fullFuncName == "main.main" { + return "main" + } else { + return fullFuncName + } + } + } + + // if we got here, we failed to find the caller's context + return "UNKNOWN_CALLER" +} + // This function is not declared with a pointer value because otherwise // race conditions will occur when using multiple goroutines func (entry Entry) log(level Level, msg string) { @@ -92,7 +131,9 @@ func (entry Entry) log(level Level, msg string) { entry.Time = time.Now() entry.Level = level entry.Message = msg - + if ReportMethod() { + entry.Method = getCaller() + } if err := entry.Logger.Hooks.Fire(level, &entry); err != nil { entry.Logger.mu.Lock() fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) diff --git a/exported.go b/exported.go index 9a0120ac1..7a02d7cfa 100644 --- a/exported.go +++ b/exported.go @@ -27,6 +27,22 @@ func SetFormatter(formatter Formatter) { std.Formatter = formatter } +// SetReportMethod sets whether to include calling method and line as +// fields +func SetReportMethod(include bool) { + std.mu.Lock() + defer std.mu.Unlock() + std.ReportMethod = include +} + +// ReportMethod sets whether to include calling method and line as +// fields +func ReportMethod() bool { + std.mu.Lock() + defer std.mu.Unlock() + return std.ReportMethod +} + // SetLevel sets the standard logger level. func SetLevel(level Level) { std.mu.Lock() diff --git a/formatter.go b/formatter.go index b5fbe934d..c48e02fb3 100644 --- a/formatter.go +++ b/formatter.go @@ -2,6 +2,7 @@ package logrus import "time" +// DefaultTimestampFormat is YYYY-mm-DDTHH:MM:SS-TZ const DefaultTimestampFormat = time.RFC3339 // The Formatter interface is used to implement a custom Formatter. It takes an @@ -18,7 +19,7 @@ type Formatter interface { Format(*Entry) ([]byte, error) } -// This is to not silently overwrite `time`, `msg` and `level` fields when +// This is to not silently overwrite `time`, `msg`, `method` and `level` fields when // dumping it. If this code wasn't there doing: // // logrus.WithField("level", 1).Info("hello") @@ -42,4 +43,11 @@ func prefixFieldClashes(data Fields) { if l, ok := data["level"]; ok { data["fields.level"] = l } + + // If ReportMethod is not set, 'method' will not conflict. + if ReportMethod() { + if l, ok := data["method"]; ok { + data["fields.method"] = l + } + } } diff --git a/json_formatter.go b/json_formatter.go index f3729bff7..a84cc155e 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -9,9 +9,10 @@ type fieldKey string type FieldMap map[fieldKey]string const ( - FieldKeyMsg = "msg" - FieldKeyLevel = "level" - FieldKeyTime = "time" + FieldKeyMsg = "msg" + FieldKeyLevel = "level" + FieldKeyTime = "time" + FieldKeyMethod = "method" ) func (f FieldMap) resolve(key fieldKey) string { @@ -30,16 +31,17 @@ type JSONFormatter struct { // As an example: // formatter := &JSONFormatter{ // FieldMap: FieldMap{ - // FieldKeyTime: "@timestamp", - // FieldKeyLevel: "@level", - // FieldKeyLevel: "@message", + // FieldKeyTime: "@timestamp", + // FieldKeyLevel: "@level", + // FieldKeyMsg: "@message", + // FieldKeyMethod: "@caller", // }, // } FieldMap FieldMap } func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { - data := make(Fields, len(entry.Data)+3) + data := make(Fields, len(entry.Data)+4) for k, v := range entry.Data { switch v := v.(type) { case error: @@ -60,7 +62,9 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat) data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() - + if ReportMethod() { + data[f.FieldMap.resolve(FieldKeyMethod)] = entry.Method + } serialized, err := json.Marshal(data) if err != nil { return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) diff --git a/json_formatter_test.go b/json_formatter_test.go index 5baa93e4b..dc09bfef2 100644 --- a/json_formatter_test.go +++ b/json_formatter_test.go @@ -169,3 +169,51 @@ func TestJSONTimeKey(t *testing.T) { t.Fatal("Expected JSON to format time key") } } + +func TestFieldDoesNotClashWithMethod(t *testing.T) { + SetReportMethod(false) + formatter := &JSONFormatter{} + + b, err := formatter.Format(WithField("method", "howdy pardner")) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + + entry := make(map[string]interface{}) + err = json.Unmarshal(b, &entry) + if err != nil { + t.Fatal("Unable to unmarshal formatted entry: ", err) + } + + if entry["method"] != "howdy pardner" { + t.Fatal("method field replaced when ReportMethod=false") + } +} + +func TestFieldClashWithMethod(t *testing.T) { + SetReportMethod(true) + formatter := &JSONFormatter{} + + b, err := formatter.Format(WithField("method", "howdy pardner")) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + + entry := make(map[string]interface{}) + err = json.Unmarshal(b, &entry) + if err != nil { + t.Fatal("Unable to unmarshal formatted entry: ", err) + } + + if entry["fields.method"] != "howdy pardner" { + t.Fatalf("fields.method not set to original method field when ReportMethod=true (got '%s')", + entry["fields.method"]) + } + + if entry["method"] != "" { // since we didn't actually log, it's set to the empty string + t.Fatalf("method not set as expected when ReportMethod=true (got '%s')", + entry["method"]) + } + + SetReportMethod(false) // return to default value +} diff --git a/logger.go b/logger.go index b769f3d35..64967c423 100644 --- a/logger.go +++ b/logger.go @@ -22,6 +22,10 @@ type Logger struct { // own that implements the `Formatter` interface, see the `README` or included // formatters for examples. Formatter Formatter + + //Flag for whether to log caller info (off by default) + ReportMethod bool + // 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 @@ -67,10 +71,11 @@ func (mw *MutexWrap) Disable() { // It's recommended to make this a global instance called `log`. func New() *Logger { return &Logger{ - Out: os.Stderr, - Formatter: new(TextFormatter), - Hooks: make(LevelHooks), - Level: InfoLevel, + Out: os.Stderr, + Formatter: new(TextFormatter), + Hooks: make(LevelHooks), + Level: InfoLevel, + ReportMethod: false, } } diff --git a/logrus_test.go b/logrus_test.go index bfc478055..2ba154e4f 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -56,6 +56,30 @@ func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields ma assertions(fields) } +// TestReportMethod verifies that when ReportMethod is set, the 'method' field +// is added, and when it is unset it is not set or modified +func TestReportMethod(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + SetReportMethod(false) + log.Print("testNoCaller") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "testNoCaller") + assert.Equal(t, fields["level"], "info") + assert.Equal(t, fields["method"], nil) + }) + + LogAndAssertJSON(t, func(log *Logger) { + SetReportMethod(true) + log.Print("testWithCaller") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "testWithCaller") + assert.Equal(t, fields["level"], "info") + assert.Equal(t, fields["method"], "testing.tRunner") + }) + + SetReportMethod(false) // return to default value +} + func TestPrint(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.Print("test") @@ -241,6 +265,55 @@ func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) { } +func TestNestedLoggingReportsCorrectCaller(t *testing.T) { + var buffer bytes.Buffer + var fields Fields + + SetReportMethod(true) + logger := New() + logger.Out = &buffer + logger.Formatter = new(JSONFormatter) + + llog := logger.WithField("context", "eating raw fish") + + llog.Info("looks delicious") + + err := json.Unmarshal(buffer.Bytes(), &fields) + assert.NoError(t, err, "should have decoded first message") + assert.Equal(t, len(fields), 5, "should have msg/time/level/method/context fields") + assert.Equal(t, fields["msg"], "looks delicious") + assert.Equal(t, fields["context"], "eating raw fish") + assert.Equal(t, fields["method"], "testing.tRunner") + + buffer.Reset() + + logger.WithFields(Fields{ + "foo": "a", + }).WithFields(Fields{ + "bar": "b", + }).WithFields(Fields{ + "baz": "c", + }).WithFields(Fields{ + "method": "man", + }).WithFields(Fields{ + "clan": "Wu Tang", + }).Print("omg it is!") + + err = json.Unmarshal(buffer.Bytes(), &fields) + assert.NoError(t, err, "should have decoded second message") + assert.Equal(t, 10, len(fields), "should have all builtin fields plus foo,bar,baz") + assert.Equal(t, "omg it is!", fields["msg"]) + assert.Equal(t, "a", fields["foo"]) + assert.Equal(t, "b", fields["bar"]) + assert.Equal(t, "c", fields["baz"]) + assert.Equal(t, "man", fields["fields.method"]) + assert.Equal(t, "Wu Tang", fields["clan"]) + assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry") + assert.Equal(t, "testing.tRunner", fields["method"]) + + SetReportMethod(false) // return to default value +} + func TestConvertLevelToString(t *testing.T) { assert.Equal(t, "debug", DebugLevel.String()) assert.Equal(t, "info", InfoLevel.String()) diff --git a/text_formatter.go b/text_formatter.go index 9114b3ca4..4b3cc6210 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -88,6 +88,9 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat)) } f.appendKeyValue(b, "level", entry.Level.String()) + if ReportMethod() { + f.appendKeyValue(b, "method", entry.Method) + } if entry.Message != "" { f.appendKeyValue(b, "msg", entry.Message) } @@ -115,10 +118,17 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin levelText := strings.ToUpper(entry.Level.String())[0:4] + caller := "" + if ReportMethod() { + caller = fmt.Sprintf(" %s()", entry.Method) + } + if !f.FullTimestamp { - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, + miniTS(), caller, entry.Message) } else { - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message) + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, + entry.Time.Format(timestampFormat), caller, entry.Message) } for _, k := range keys { v := entry.Data[k] From 9f61c1def8766342ddc15af8bcf586ff32c61a12 Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Mon, 28 Nov 2016 13:43:38 -0800 Subject: [PATCH 168/547] Add README notes and CHANGELOG entries documentation for usage of the new optional calling-method logging --- CHANGELOG.md | 4 ++++ README.md | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2c2bc211..0d84a2fd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.11.0 + +* feature: Add optional logging of caller method + # 0.10.0 * feature: Add a test hook (#180) diff --git a/README.md b/README.md index 275c1131a..57f15e463 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,9 @@ time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4 time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009 time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true -exit status 1 ``` To ensure this behaviour even if a TTY is attached, set your formatter as follows: + ``` log.SetFormatter(&log.TextFormatter{ DisableColors: true, @@ -53,6 +53,22 @@ To ensure this behaviour even if a TTY is attached, set your formatter as follow }) ``` +If you wish to add the calling method as a field, instruct the logger via: +``` +log.SetReportMethod(true) +``` +This adds the caller as 'method' like so: + +```json +{"animal":"penguin","level":"fatal","method":"arcticcreatures.migrate","msg":"a penguin swims by", +"time":"2014-03-10 19:57:38.562543129 -0400 EDT"} + +``` + +```text +time="2015-03-26T01:27:38-04:00" level=fatal method=arcticcreatures.migrate msg="a penguin swims by" animal=penguin + +``` #### Example The simplest way to use Logrus is simply the package-level exported logger: From 00c4f41f4fd7a5b4be1fd71fe5eabcc9dbedb55a Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Mon, 28 Nov 2016 14:47:38 -0800 Subject: [PATCH 169/547] performance: precompile regex before iterating --- entry.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/entry.go b/entry.go index 1e721cefe..290232a8f 100644 --- a/entry.go +++ b/entry.go @@ -96,6 +96,11 @@ func getCaller() (method string) { // Restrict the lookback to 25 frames - if it's further than that, report UNKNOWN pcs := make([]uintptr, 25) + matchesLogrus, err := regexp.Compile("logrus.*") + if err != nil { + return "CALLER_LOOKUP_FAILED" + } + // the first non-logrus caller is at least three frames away depth := runtime.Callers(3, pcs) for i := 0; i < depth; i++ { @@ -105,10 +110,7 @@ func getCaller() (method string) { fullFuncName = fullFuncName[idx:] } - matched, err := regexp.MatchString("logrus.*", fullFuncName) - if err != nil { - return "CALLER_LOOKUP_FAILED" - } + matched := matchesLogrus.MatchString(fullFuncName) // If the caller isn't part of logrus, we're done if !matched { From 4f87dbc7510902e0a79b8333829b76c4bf89949d Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Mon, 28 Nov 2016 16:22:33 -0800 Subject: [PATCH 170/547] push compilation even higher, to reduce to one call --- entry.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/entry.go b/entry.go index 290232a8f..b7f4e9cb8 100644 --- a/entry.go +++ b/entry.go @@ -13,12 +13,17 @@ import ( var bufferPool *sync.Pool +// regex for validation is external, to reduce compilation overhead +var matchesLogrus *regexp.Regexp + func init() { bufferPool = &sync.Pool{ New: func() interface{} { return new(bytes.Buffer) }, } + + matchesLogrus, _ = regexp.Compile("logrus.*") } // Defines the key when adding errors using WithError. @@ -51,11 +56,13 @@ type Entry struct { } func NewEntry(logger *Logger) *Entry { - return &Entry{ + entry := &Entry{ Logger: logger, // Default is three fields, plus one optional. Give a little extra room. Data: make(Fields, 6), } + + return entry } // Returns the string representation from the reader and ultimately the @@ -96,11 +103,6 @@ func getCaller() (method string) { // Restrict the lookback to 25 frames - if it's further than that, report UNKNOWN pcs := make([]uintptr, 25) - matchesLogrus, err := regexp.Compile("logrus.*") - if err != nil { - return "CALLER_LOOKUP_FAILED" - } - // the first non-logrus caller is at least three frames away depth := runtime.Callers(3, pcs) for i := 0; i < depth; i++ { From bc1d06726dde4a70c3fa2facef8685f2864b13cb Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Tue, 29 Nov 2016 09:35:34 -0800 Subject: [PATCH 171/547] doc updates, and relabel ReportMethod in the Logrus context it's the caller, so use that internally. Label stays as 'method' since in the context of the log event that seems more correct. --- README.md | 2 +- entry.go | 6 +++--- exported.go | 14 ++++++-------- formatter.go | 4 ++-- json_formatter.go | 8 ++++---- json_formatter_test.go | 16 ++++++++-------- logger.go | 4 ++-- logrus_test.go | 14 +++++++------- text_formatter.go | 8 ++++---- 9 files changed, 37 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 57f15e463..25cc2c5a9 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ To ensure this behaviour even if a TTY is attached, set your formatter as follow If you wish to add the calling method as a field, instruct the logger via: ``` -log.SetReportMethod(true) +log.SetReportCaller(true) ``` This adds the caller as 'method' like so: diff --git a/entry.go b/entry.go index b7f4e9cb8..7949e1fc5 100644 --- a/entry.go +++ b/entry.go @@ -46,7 +46,7 @@ type Entry struct { Level Level // Calling method, with package name - Method string + Caller string // Message passed to Debug, Info, Warn, Error, Fatal or Panic Message string @@ -135,8 +135,8 @@ func (entry Entry) log(level Level, msg string) { entry.Time = time.Now() entry.Level = level entry.Message = msg - if ReportMethod() { - entry.Method = getCaller() + if ReportCaller() { + entry.Caller = getCaller() } if err := entry.Logger.Hooks.Fire(level, &entry); err != nil { entry.Logger.mu.Lock() diff --git a/exported.go b/exported.go index 7a02d7cfa..356d4a944 100644 --- a/exported.go +++ b/exported.go @@ -27,20 +27,18 @@ func SetFormatter(formatter Formatter) { std.Formatter = formatter } -// SetReportMethod sets whether to include calling method and line as -// fields -func SetReportMethod(include bool) { +// SetReportCaller sets whether to include the calling method as a field +func SetReportCaller(include bool) { std.mu.Lock() defer std.mu.Unlock() - std.ReportMethod = include + std.ReportCaller = include } -// ReportMethod sets whether to include calling method and line as -// fields -func ReportMethod() bool { +// ReportCaller returns the 'include calling method' state +func ReportCaller() bool { std.mu.Lock() defer std.mu.Unlock() - return std.ReportMethod + return std.ReportCaller } // SetLevel sets the standard logger level. diff --git a/formatter.go b/formatter.go index c48e02fb3..9e94689dc 100644 --- a/formatter.go +++ b/formatter.go @@ -44,8 +44,8 @@ func prefixFieldClashes(data Fields) { data["fields.level"] = l } - // If ReportMethod is not set, 'method' will not conflict. - if ReportMethod() { + // If Reportmethod is not set, 'method' will not conflict. + if ReportCaller() { if l, ok := data["method"]; ok { data["fields.method"] = l } diff --git a/json_formatter.go b/json_formatter.go index a84cc155e..2bb3a6ad7 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -12,7 +12,7 @@ const ( FieldKeyMsg = "msg" FieldKeyLevel = "level" FieldKeyTime = "time" - FieldKeyMethod = "method" + FieldKeyCaller = "method" ) func (f FieldMap) resolve(key fieldKey) string { @@ -34,7 +34,7 @@ type JSONFormatter struct { // FieldKeyTime: "@timestamp", // FieldKeyLevel: "@level", // FieldKeyMsg: "@message", - // FieldKeyMethod: "@caller", + // FieldKeyCaller: "@caller", // }, // } FieldMap FieldMap @@ -62,8 +62,8 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat) data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() - if ReportMethod() { - data[f.FieldMap.resolve(FieldKeyMethod)] = entry.Method + if ReportCaller() { + data[f.FieldMap.resolve(FieldKeyCaller)] = entry.Caller } serialized, err := json.Marshal(data) if err != nil { diff --git a/json_formatter_test.go b/json_formatter_test.go index dc09bfef2..ab52095f0 100644 --- a/json_formatter_test.go +++ b/json_formatter_test.go @@ -170,8 +170,8 @@ func TestJSONTimeKey(t *testing.T) { } } -func TestFieldDoesNotClashWithMethod(t *testing.T) { - SetReportMethod(false) +func TestFieldDoesNotClashWithCaller(t *testing.T) { + SetReportCaller(false) formatter := &JSONFormatter{} b, err := formatter.Format(WithField("method", "howdy pardner")) @@ -186,12 +186,12 @@ func TestFieldDoesNotClashWithMethod(t *testing.T) { } if entry["method"] != "howdy pardner" { - t.Fatal("method field replaced when ReportMethod=false") + t.Fatal("method field replaced when ReportCaller=false") } } -func TestFieldClashWithMethod(t *testing.T) { - SetReportMethod(true) +func TestFieldClashWithCaller(t *testing.T) { + SetReportCaller(true) formatter := &JSONFormatter{} b, err := formatter.Format(WithField("method", "howdy pardner")) @@ -206,14 +206,14 @@ func TestFieldClashWithMethod(t *testing.T) { } if entry["fields.method"] != "howdy pardner" { - t.Fatalf("fields.method not set to original method field when ReportMethod=true (got '%s')", + t.Fatalf("fields.method not set to original method field when ReportCaller=true (got '%s')", entry["fields.method"]) } if entry["method"] != "" { // since we didn't actually log, it's set to the empty string - t.Fatalf("method not set as expected when ReportMethod=true (got '%s')", + t.Fatalf("method not set as expected when ReportCaller=true (got '%s')", entry["method"]) } - SetReportMethod(false) // return to default value + SetReportCaller(false) // return to default value } diff --git a/logger.go b/logger.go index 64967c423..ddab68af7 100644 --- a/logger.go +++ b/logger.go @@ -24,7 +24,7 @@ type Logger struct { Formatter Formatter //Flag for whether to log caller info (off by default) - ReportMethod bool + ReportCaller bool // 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 @@ -75,7 +75,7 @@ func New() *Logger { Formatter: new(TextFormatter), Hooks: make(LevelHooks), Level: InfoLevel, - ReportMethod: false, + ReportCaller: false, } } diff --git a/logrus_test.go b/logrus_test.go index 2ba154e4f..760a24b54 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -56,11 +56,11 @@ func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields ma assertions(fields) } -// TestReportMethod verifies that when ReportMethod is set, the 'method' field +// TestReportCaller verifies that when ReportCaller is set, the 'method' field // is added, and when it is unset it is not set or modified -func TestReportMethod(t *testing.T) { +func TestReportCaller(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { - SetReportMethod(false) + SetReportCaller(false) log.Print("testNoCaller") }, func(fields Fields) { assert.Equal(t, fields["msg"], "testNoCaller") @@ -69,7 +69,7 @@ func TestReportMethod(t *testing.T) { }) LogAndAssertJSON(t, func(log *Logger) { - SetReportMethod(true) + SetReportCaller(true) log.Print("testWithCaller") }, func(fields Fields) { assert.Equal(t, fields["msg"], "testWithCaller") @@ -77,7 +77,7 @@ func TestReportMethod(t *testing.T) { assert.Equal(t, fields["method"], "testing.tRunner") }) - SetReportMethod(false) // return to default value + SetReportCaller(false) // return to default value } func TestPrint(t *testing.T) { @@ -269,7 +269,7 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) { var buffer bytes.Buffer var fields Fields - SetReportMethod(true) + SetReportCaller(true) logger := New() logger.Out = &buffer logger.Formatter = new(JSONFormatter) @@ -311,7 +311,7 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) { assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry") assert.Equal(t, "testing.tRunner", fields["method"]) - SetReportMethod(false) // return to default value + SetReportCaller(false) // return to default value } func TestConvertLevelToString(t *testing.T) { diff --git a/text_formatter.go b/text_formatter.go index 4b3cc6210..b00345115 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -88,8 +88,8 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat)) } f.appendKeyValue(b, "level", entry.Level.String()) - if ReportMethod() { - f.appendKeyValue(b, "method", entry.Method) + if ReportCaller() { + f.appendKeyValue(b, "method", entry.Caller) } if entry.Message != "" { f.appendKeyValue(b, "msg", entry.Message) @@ -119,8 +119,8 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin levelText := strings.ToUpper(entry.Level.String())[0:4] caller := "" - if ReportMethod() { - caller = fmt.Sprintf(" %s()", entry.Method) + if ReportCaller() { + caller = fmt.Sprintf(" %s()", entry.Caller) } if !f.FullTimestamp { From 0aa08ab2ad967afcda5013e5f557c3ed373d4e83 Mon Sep 17 00:00:00 2001 From: Daniel Taylor Date: Tue, 29 Nov 2016 10:52:28 -0800 Subject: [PATCH 172/547] Fix typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f9cfb0a5d..ed1b14036 100644 --- a/README.md +++ b/README.md @@ -408,7 +408,7 @@ logrus.RegisterExitHandler(handler) ... ``` -#### Thread safty +#### Thread safety By default Logger is protected by mutex for concurrent writes, this mutex is invoked when calling hooks and writing logs. If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking. From 7f27b578d64b7e878d6f4cbeaac5fc88d4848636 Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Wed, 30 Nov 2016 10:47:03 -0800 Subject: [PATCH 173/547] fix test description --- logrus_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logrus_test.go b/logrus_test.go index 760a24b54..ec3185013 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -301,7 +301,7 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) { err = json.Unmarshal(buffer.Bytes(), &fields) assert.NoError(t, err, "should have decoded second message") - assert.Equal(t, 10, len(fields), "should have all builtin fields plus foo,bar,baz") + assert.Equal(t, 10, len(fields), "should have all builtin fields plus foo,bar,baz,...") assert.Equal(t, "omg it is!", fields["msg"]) assert.Equal(t, "a", fields["foo"]) assert.Equal(t, "b", fields["bar"]) From 3358e77b1b4dc7e223bcda3e8fac1df101709052 Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Wed, 30 Nov 2016 11:36:48 -0800 Subject: [PATCH 174/547] revert slight added complexity in NewEntry() --- entry.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/entry.go b/entry.go index 7949e1fc5..850b958e2 100644 --- a/entry.go +++ b/entry.go @@ -56,13 +56,11 @@ type Entry struct { } func NewEntry(logger *Logger) *Entry { - entry := &Entry{ + return &Entry{ Logger: logger, // Default is three fields, plus one optional. Give a little extra room. Data: make(Fields, 6), } - - return entry } // Returns the string representation from the reader and ultimately the From 741542d333417e2c82876586239df904f39ec441 Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Wed, 30 Nov 2016 14:07:10 -0800 Subject: [PATCH 175/547] responses to review comments - empty string as marker for failure to discover calling function - tighten up logger usage - don't rely on std logger internally Also fix ordering of expected/got in logrus_test.go to ensure correct output form test failures. --- entry.go | 4 +-- exported.go | 10 ++----- formatter.go | 6 ++-- json_formatter.go | 10 +++++-- json_formatter_test.go | 1 + logrus_test.go | 68 ++++++++++++++++++++---------------------- text_formatter.go | 17 +++++++++-- 7 files changed, 63 insertions(+), 53 deletions(-) diff --git a/entry.go b/entry.go index 850b958e2..9e866872b 100644 --- a/entry.go +++ b/entry.go @@ -123,7 +123,7 @@ func getCaller() (method string) { } // if we got here, we failed to find the caller's context - return "UNKNOWN_CALLER" + return "" } // This function is not declared with a pointer value because otherwise @@ -133,7 +133,7 @@ func (entry Entry) log(level Level, msg string) { entry.Time = time.Now() entry.Level = level entry.Message = msg - if ReportCaller() { + if entry.Logger.ReportCaller { entry.Caller = getCaller() } if err := entry.Logger.Hooks.Fire(level, &entry); err != nil { diff --git a/exported.go b/exported.go index 356d4a944..3585f9419 100644 --- a/exported.go +++ b/exported.go @@ -27,20 +27,14 @@ func SetFormatter(formatter Formatter) { std.Formatter = formatter } -// SetReportCaller sets whether to include the calling method as a field +// SetReportCaller sets whether the standard logger will include the calling +// method as a field. func SetReportCaller(include bool) { std.mu.Lock() defer std.mu.Unlock() std.ReportCaller = include } -// ReportCaller returns the 'include calling method' state -func ReportCaller() bool { - std.mu.Lock() - defer std.mu.Unlock() - return std.ReportCaller -} - // SetLevel sets the standard logger level. func SetLevel(level Level) { std.mu.Lock() diff --git a/formatter.go b/formatter.go index 9e94689dc..773b359db 100644 --- a/formatter.go +++ b/formatter.go @@ -31,7 +31,7 @@ type Formatter interface { // // It's not exported because it's still using Data in an opinionated way. It's to // avoid code duplication between the two default formatters. -func prefixFieldClashes(data Fields) { +func prefixFieldClashes(data Fields, reportCaller bool) { if t, ok := data["time"]; ok { data["fields.time"] = t } @@ -44,8 +44,8 @@ func prefixFieldClashes(data Fields) { data["fields.level"] = l } - // If Reportmethod is not set, 'method' will not conflict. - if ReportCaller() { + // If reportCaller is not set, 'method' will not conflict. + if reportCaller { if l, ok := data["method"]; ok { data["fields.method"] = l } diff --git a/json_formatter.go b/json_formatter.go index 2bb3a6ad7..46498dad5 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -52,7 +52,13 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { data[k] = v } } - prefixFieldClashes(data) + + reportCaller := false + if entry.Logger != nil { + reportCaller = entry.Logger.ReportCaller + } + + prefixFieldClashes(data, reportCaller) timestampFormat := f.TimestampFormat if timestampFormat == "" { @@ -62,7 +68,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat) data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() - if ReportCaller() { + if reportCaller { data[f.FieldMap.resolve(FieldKeyCaller)] = entry.Caller } serialized, err := json.Marshal(data) diff --git a/json_formatter_test.go b/json_formatter_test.go index ab52095f0..823522482 100644 --- a/json_formatter_test.go +++ b/json_formatter_test.go @@ -193,6 +193,7 @@ func TestFieldDoesNotClashWithCaller(t *testing.T) { func TestFieldClashWithCaller(t *testing.T) { SetReportCaller(true) formatter := &JSONFormatter{} + std.ReportCaller = true b, err := formatter.Format(WithField("method", "howdy pardner")) if err != nil { diff --git a/logrus_test.go b/logrus_test.go index ec3185013..40cbd644b 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -60,32 +60,30 @@ func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields ma // is added, and when it is unset it is not set or modified func TestReportCaller(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { - SetReportCaller(false) + log.ReportCaller = false log.Print("testNoCaller") }, func(fields Fields) { - assert.Equal(t, fields["msg"], "testNoCaller") - assert.Equal(t, fields["level"], "info") - assert.Equal(t, fields["method"], nil) + assert.Equal(t, "testNoCaller", fields["msg"]) + assert.Equal(t, "info", fields["level"]) + assert.Equal(t, nil, fields["method"]) }) LogAndAssertJSON(t, func(log *Logger) { - SetReportCaller(true) + log.ReportCaller = true log.Print("testWithCaller") }, func(fields Fields) { - assert.Equal(t, fields["msg"], "testWithCaller") - assert.Equal(t, fields["level"], "info") - assert.Equal(t, fields["method"], "testing.tRunner") + assert.Equal(t, "testWithCaller", fields["msg"]) + assert.Equal(t, "info", fields["level"]) + assert.Equal(t, "testing.tRunner", fields["method"]) }) - - SetReportCaller(false) // return to default value } func TestPrint(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.Print("test") }, func(fields Fields) { - assert.Equal(t, fields["msg"], "test") - assert.Equal(t, fields["level"], "info") + assert.Equal(t, "test", fields["msg"]) + assert.Equal(t, "info", fields["level"]) }) } @@ -93,8 +91,8 @@ func TestInfo(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.Info("test") }, func(fields Fields) { - assert.Equal(t, fields["msg"], "test") - assert.Equal(t, fields["level"], "info") + assert.Equal(t, "test", fields["msg"]) + assert.Equal(t, "info", fields["level"]) }) } @@ -102,8 +100,8 @@ func TestWarn(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.Warn("test") }, func(fields Fields) { - assert.Equal(t, fields["msg"], "test") - assert.Equal(t, fields["level"], "warning") + assert.Equal(t, "test", fields["msg"]) + assert.Equal(t, "warning", fields["level"]) }) } @@ -111,7 +109,7 @@ func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.Infoln("test", "test") }, func(fields Fields) { - assert.Equal(t, fields["msg"], "test test") + assert.Equal(t, "test test", fields["msg"]) }) } @@ -119,7 +117,7 @@ func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.Infoln("test", 10) }, func(fields Fields) { - assert.Equal(t, fields["msg"], "test 10") + assert.Equal(t, "test 10", fields["msg"]) }) } @@ -127,7 +125,7 @@ func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.Infoln(10, 10) }, func(fields Fields) { - assert.Equal(t, fields["msg"], "10 10") + assert.Equal(t, "10 10", fields["msg"]) }) } @@ -135,7 +133,7 @@ func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.Infoln(10, 10) }, func(fields Fields) { - assert.Equal(t, fields["msg"], "10 10") + assert.Equal(t, "10 10", fields["msg"]) }) } @@ -143,7 +141,7 @@ func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.Info("test", 10) }, func(fields Fields) { - assert.Equal(t, fields["msg"], "test10") + assert.Equal(t, "test10", fields["msg"]) }) } @@ -151,7 +149,7 @@ func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.Info("test", "test") }, func(fields Fields) { - assert.Equal(t, fields["msg"], "testtest") + assert.Equal(t, "testtest", fields["msg"]) }) } @@ -189,7 +187,7 @@ func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.WithField("msg", "hello").Info("test") }, func(fields Fields) { - assert.Equal(t, fields["msg"], "test") + assert.Equal(t, "test", fields["msg"]) }) } @@ -197,8 +195,8 @@ func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.WithField("msg", "hello").Info("test") }, func(fields Fields) { - assert.Equal(t, fields["msg"], "test") - assert.Equal(t, fields["fields.msg"], "hello") + assert.Equal(t, "test", fields["msg"]) + assert.Equal(t, "hello", fields["fields.msg"]) }) } @@ -206,7 +204,7 @@ func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.WithField("time", "hello").Info("test") }, func(fields Fields) { - assert.Equal(t, fields["fields.time"], "hello") + assert.Equal(t, "hello", fields["fields.time"]) }) } @@ -214,8 +212,8 @@ func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.WithField("level", 1).Info("test") }, func(fields Fields) { - assert.Equal(t, fields["level"], "info") - assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only + assert.Equal(t, "info", fields["level"]) + assert.Equal(t, 1.0, fields["fields.level"]) // JSON has floats only }) } @@ -259,8 +257,8 @@ func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) { err = json.Unmarshal(buffer.Bytes(), &fields) assert.NoError(t, err, "should have decoded second message") assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields") - assert.Equal(t, fields["msg"], "omg it is!") - assert.Equal(t, fields["context"], "eating raw fish") + assert.Equal(t, "omg it is!", fields["msg"]) + assert.Equal(t, "eating raw fish", fields["context"]) assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry") } @@ -269,10 +267,10 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) { var buffer bytes.Buffer var fields Fields - SetReportCaller(true) logger := New() logger.Out = &buffer logger.Formatter = new(JSONFormatter) + logger.ReportCaller = true llog := logger.WithField("context", "eating raw fish") @@ -281,9 +279,9 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) { err := json.Unmarshal(buffer.Bytes(), &fields) assert.NoError(t, err, "should have decoded first message") assert.Equal(t, len(fields), 5, "should have msg/time/level/method/context fields") - assert.Equal(t, fields["msg"], "looks delicious") - assert.Equal(t, fields["context"], "eating raw fish") - assert.Equal(t, fields["method"], "testing.tRunner") + assert.Equal(t, "looks delicious", fields["msg"]) + assert.Equal(t, "eating raw fish", fields["context"]) + assert.Equal(t, "testing.tRunner", fields["method"]) buffer.Reset() @@ -311,7 +309,7 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) { assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry") assert.Equal(t, "testing.tRunner", fields["method"]) - SetReportCaller(false) // return to default value + logger.ReportCaller = false // return to default value } func TestConvertLevelToString(t *testing.T) { diff --git a/text_formatter.go b/text_formatter.go index b00345115..91715f80d 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -72,7 +72,12 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { b = &bytes.Buffer{} } - prefixFieldClashes(entry.Data) + reportCaller := false + if entry.Logger != nil { + reportCaller = entry.Logger.ReportCaller + } + + prefixFieldClashes(entry.Data, reportCaller) isColorTerminal := isTerminal && (runtime.GOOS != "windows") isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors @@ -88,7 +93,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat)) } f.appendKeyValue(b, "level", entry.Level.String()) - if ReportCaller() { + if reportCaller { f.appendKeyValue(b, "method", entry.Caller) } if entry.Message != "" { @@ -119,7 +124,13 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin levelText := strings.ToUpper(entry.Level.String())[0:4] caller := "" - if ReportCaller() { + + reportCaller := false + if entry.Logger != nil { + reportCaller = entry.Logger.ReportCaller + } + + if reportCaller { caller = fmt.Sprintf(" %s()", entry.Caller) } From c251e1d5d5ba076a70ab4efaff1b96345f3f1278 Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Wed, 30 Nov 2016 15:15:38 -0800 Subject: [PATCH 176/547] simplify hasCaller check --- entry.go | 6 ++++++ json_formatter.go | 9 ++------- json_formatter_test.go | 8 ++++---- text_formatter.go | 16 +++------------- 4 files changed, 15 insertions(+), 24 deletions(-) diff --git a/entry.go b/entry.go index 9e866872b..d3b1aa97d 100644 --- a/entry.go +++ b/entry.go @@ -126,6 +126,12 @@ func getCaller() (method string) { return "" } +func (entry Entry) HasCaller() (has bool) { + return entry.Logger != nil && + entry.Logger.ReportCaller && + entry.Caller != "" +} + // This function is not declared with a pointer value because otherwise // race conditions will occur when using multiple goroutines func (entry Entry) log(level Level, msg string) { diff --git a/json_formatter.go b/json_formatter.go index 46498dad5..efb9a9a05 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -53,12 +53,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { } } - reportCaller := false - if entry.Logger != nil { - reportCaller = entry.Logger.ReportCaller - } - - prefixFieldClashes(data, reportCaller) + prefixFieldClashes(data, entry.HasCaller()) timestampFormat := f.TimestampFormat if timestampFormat == "" { @@ -68,7 +63,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat) data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() - if reportCaller { + if entry.HasCaller() { data[f.FieldMap.resolve(FieldKeyCaller)] = entry.Caller } serialized, err := json.Marshal(data) diff --git a/json_formatter_test.go b/json_formatter_test.go index 823522482..05cd51e13 100644 --- a/json_formatter_test.go +++ b/json_formatter_test.go @@ -193,9 +193,9 @@ func TestFieldDoesNotClashWithCaller(t *testing.T) { func TestFieldClashWithCaller(t *testing.T) { SetReportCaller(true) formatter := &JSONFormatter{} - std.ReportCaller = true - - b, err := formatter.Format(WithField("method", "howdy pardner")) + e := WithField("method", "howdy pardner") + e.Caller = "somefunc" + b, err := formatter.Format(e) if err != nil { t.Fatal("Unable to format entry: ", err) } @@ -211,7 +211,7 @@ func TestFieldClashWithCaller(t *testing.T) { entry["fields.method"]) } - if entry["method"] != "" { // since we didn't actually log, it's set to the empty string + if entry["method"] != "somefunc" { t.Fatalf("method not set as expected when ReportCaller=true (got '%s')", entry["method"]) } diff --git a/text_formatter.go b/text_formatter.go index 91715f80d..b0fa789ed 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -72,12 +72,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { b = &bytes.Buffer{} } - reportCaller := false - if entry.Logger != nil { - reportCaller = entry.Logger.ReportCaller - } - - prefixFieldClashes(entry.Data, reportCaller) + prefixFieldClashes(entry.Data, entry.HasCaller()) isColorTerminal := isTerminal && (runtime.GOOS != "windows") isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors @@ -93,7 +88,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat)) } f.appendKeyValue(b, "level", entry.Level.String()) - if reportCaller { + if entry.HasCaller() { f.appendKeyValue(b, "method", entry.Caller) } if entry.Message != "" { @@ -125,12 +120,7 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin caller := "" - reportCaller := false - if entry.Logger != nil { - reportCaller = entry.Logger.ReportCaller - } - - if reportCaller { + if entry.HasCaller() { caller = fmt.Sprintf(" %s()", entry.Caller) } From 9e712947d6806a163215fd5bf2348dd41d999628 Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Thu, 1 Dec 2016 11:12:44 -0800 Subject: [PATCH 177/547] tweak timing tests to handle slower VMs and older GoLang --- logrus_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logrus_test.go b/logrus_test.go index 6765acf28..58d9981a2 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -328,7 +328,7 @@ func logLoop(iterations int, reportCaller bool) { // Assertions for upper bounds to reporting overhead func TestCallerReportingOverhead(t *testing.T) { - iterations := 10000 + iterations := 5000 before := time.Now() logLoop(iterations, false) during := time.Now() From be2d0c690a6e053af69263f27f2c21e0e27e1b1e Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 1 Dec 2016 18:53:16 -0500 Subject: [PATCH 178/547] Revert "Merge pull request #384 from mnzt/master" This reverts commit 3ad6ae687af82bac6f59d165818e715990297f62, reversing changes made to ac66b27b7be1a6f57a1b11cd715d98e01588a9bc. --- README.md | 18 +++++++++--------- alt_exit_test.go | 2 +- doc.go | 4 ++-- examples/basic/basic.go | 2 +- examples/hook/hook.go | 10 ++-------- hooks/syslog/README.md | 10 +++++----- hooks/syslog/syslog.go | 3 +-- hooks/syslog/syslog_test.go | 3 +-- hooks/test/test.go | 2 +- hooks/test/test_test.go | 2 +- json_formatter.go | 2 +- 11 files changed, 25 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 429725aa5..ed1b14036 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) 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 @@ -54,7 +54,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() { @@ -65,7 +65,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: @@ -74,7 +74,7 @@ package main import ( "os" - log "github.com/sirupsen/logrus" + log "github.com/Sirupsen/logrus" ) func init() { @@ -123,7 +123,7 @@ application, you can also create an instance of the `logrus` Logger: package main import ( - "github.com/sirupsen/logrus" + "github.com/Sirupsen/logrus" ) // Create a new instance of the logger. You can have any number of instances. @@ -176,9 +176,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" ) @@ -203,7 +203,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. | | [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. | +| [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. | @@ -283,7 +283,7 @@ could do: ```go import ( - log "github.com/sirupsen/logrus" + log "github.com/Sirupsen/logrus" ) init() { diff --git a/alt_exit_test.go b/alt_exit_test.go index d18296348..022b77830 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" diff --git a/doc.go b/doc.go index da67aba06..dddd5f877 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 b22468d9c..a1623ec00 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" ) var log = logrus.New() diff --git a/examples/hook/hook.go b/examples/hook/hook.go index 707a5bbf4..3187f6d3e 100644 --- a/examples/hook/hook.go +++ b/examples/hook/hook.go @@ -1,14 +1,8 @@ -// +build ignore -// Do NOT include the above line in your code. This is a build constraint used -// to prevent import loops in the code whilst go get'ting it. -// Read more about build constraints in golang here: -// https://golang.org/pkg/go/build/#hdr-Build_Constraints - package main import ( - "github.com/sirupsen/logrus" - airbrake "gopkg.in/gemnasium/logrus-airbrake-hook.v2" + "github.com/Sirupsen/logrus" + "gopkg.in/gemnasium/logrus-airbrake-hook.v2" ) var log = logrus.New() diff --git a/hooks/syslog/README.md b/hooks/syslog/README.md index 92b391c17..066704b37 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() { @@ -36,4 +36,4 @@ func main() { log.Hooks.Add(hook) } } -``` +``` \ No newline at end of file diff --git a/hooks/syslog/syslog.go b/hooks/syslog/syslog.go index fef980464..a36e20032 100644 --- a/hooks/syslog/syslog.go +++ b/hooks/syslog/syslog.go @@ -4,10 +4,9 @@ package logrus_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 89bd1ecb4..42762dc10 100644 --- a/hooks/syslog/syslog_test.go +++ b/hooks/syslog/syslog_test.go @@ -1,10 +1,9 @@ package logrus_syslog import ( + "github.com/Sirupsen/logrus" "log/syslog" "testing" - - "github.com/sirupsen/logrus" ) func TestLocalhostAddAndPrint(t *testing.T) { diff --git a/hooks/test/test.go b/hooks/test/test.go index 48c06ab23..068812535 100644 --- a/hooks/test/test.go +++ b/hooks/test/test.go @@ -3,7 +3,7 @@ package test import ( "io/ioutil" - "github.com/sirupsen/logrus" + "github.com/Sirupsen/logrus" ) // test.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 3f55cfe31..d69455ba0 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" ) diff --git a/json_formatter.go b/json_formatter.go index 47325a76d..f3729bff7 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -44,7 +44,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 a3740f33120ff35bf8c164be72ae4a43b708e661 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 1 Dec 2016 18:55:03 -0500 Subject: [PATCH 179/547] readme: apologize for casing issue --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index ed1b14036..05d678ad8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # 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 From 50ead7e4e7b3de0cba525e9e1362a0cb80dadef2 Mon Sep 17 00:00:00 2001 From: Craig Jellick Date: Thu, 1 Dec 2016 17:34:47 -0700 Subject: [PATCH 180/547] Update readme example for switching output Clarifies that stderr is the default, not stdout. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 05d678ad8..77cc72a51 100644 --- a/README.md +++ b/README.md @@ -87,8 +87,8 @@ func init() { // Log as JSON instead of the default ASCII formatter. log.SetFormatter(&log.JSONFormatter{}) - // Output to stderr instead of stdout, could also be a file. - log.SetOutput(os.Stderr) + // Output to stdout instead of the default stderr, could also be a file. + log.SetOutput(os.Stdout) // Only log the warning severity or above. log.SetLevel(log.WarnLevel) From db9a9035e6ec03c6d39bdd34544b298a6da9a1cb Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Fri, 2 Dec 2016 09:52:11 -0800 Subject: [PATCH 181/547] add note on caller-reporting overhead to README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 30402b69b..083fcdc1d 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,12 @@ This adds the caller as 'method' like so: time="2015-03-26T01:27:38-04:00" level=fatal method=arcticcreatures.migrate msg="a penguin swims by" animal=penguin ``` +Note that this does add measurable overhead - the cost will depend on the of Go, but is between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your environment via benchmarks: +``` +go test -bench=.*CallerTracing +``` + + #### Example The simplest way to use Logrus is simply the package-level exported logger: From 63640ed0c4bc2f4a51dc2b35b4963b504ff10c53 Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Fri, 2 Dec 2016 09:57:30 -0800 Subject: [PATCH 182/547] README formatting tweak --- README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 083fcdc1d..32d145b0a 100644 --- a/README.md +++ b/README.md @@ -68,17 +68,14 @@ This adds the caller as 'method' like so: ```json {"animal":"penguin","level":"fatal","method":"arcticcreatures.migrate","msg":"a penguin swims by", "time":"2014-03-10 19:57:38.562543129 -0400 EDT"} - ``` ```text time="2015-03-26T01:27:38-04:00" level=fatal method=arcticcreatures.migrate msg="a penguin swims by" animal=penguin - -``` -Note that this does add measurable overhead - the cost will depend on the of Go, but is between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your environment via benchmarks: -``` -go test -bench=.*CallerTracing ``` +Note that this does add measurable overhead - the cost will depend on the of Go, but is +between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your +environment via benchmarks: `go test -bench=.*CallerTracing` #### Example From 2b8bc2e7ef3948bece454237cd14841d55235cb1 Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Fri, 2 Dec 2016 10:09:30 -0800 Subject: [PATCH 183/547] add syntax hilighting to new example blocks --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 32d145b0a..bb71050fe 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x20822 ``` To ensure this behaviour even if a TTY is attached, set your formatter as follows: -``` +```go log.SetFormatter(&log.TextFormatter{ DisableColors: true, FullTimestamp: true, @@ -60,7 +60,7 @@ To ensure this behaviour even if a TTY is attached, set your formatter as follow ``` If you wish to add the calling method as a field, instruct the logger via: -``` +```go log.SetReportCaller(true) ``` This adds the caller as 'method' like so: @@ -75,7 +75,7 @@ time="2015-03-26T01:27:38-04:00" level=fatal method=arcticcreatures.migrate msg= ``` Note that this does add measurable overhead - the cost will depend on the of Go, but is between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your -environment via benchmarks: `go test -bench=.*CallerTracing` +environment via benchmarks: ```go test -bench=.*CallerTracing``` #### Example From 719f29e29aaefb9a7aaca7fd5ec4275fd5e17960 Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Tue, 6 Dec 2016 12:53:21 -0800 Subject: [PATCH 184/547] responses to code review - field rename to be more properly generic - drop rewrite of main.main --- README.md | 5 ++++- entry.go | 6 +----- formatter.go | 12 +++++++----- json_formatter.go | 18 +++++++++--------- json_formatter_test.go | 20 ++++++++++---------- logrus_test.go | 36 ++++++++++++++++++------------------ text_formatter.go | 2 +- 7 files changed, 50 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index bb71050fe..329e7691a 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,10 @@ time="2015-03-26T01:27:38-04:00" level=fatal method=arcticcreatures.migrate msg= ``` Note that this does add measurable overhead - the cost will depend on the of Go, but is between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your -environment via benchmarks: ```go test -bench=.*CallerTracing``` +environment via benchmarks: +``` +go test -bench=.*CallerTracing +``` #### Example diff --git a/entry.go b/entry.go index a96f11d39..50ca795d9 100644 --- a/entry.go +++ b/entry.go @@ -138,11 +138,7 @@ func getCaller() (method string) { // If the caller isn't part of this package, we're done if pkg != logrusPackage { - if fullFuncName == "main.main" { - return "main" - } else { - return fullFuncName - } + return fullFuncName } } diff --git a/formatter.go b/formatter.go index 773b359db..ae319c34b 100644 --- a/formatter.go +++ b/formatter.go @@ -1,6 +1,8 @@ package logrus -import "time" +import ( + "time" +) // DefaultTimestampFormat is YYYY-mm-DDTHH:MM:SS-TZ const DefaultTimestampFormat = time.RFC3339 @@ -19,7 +21,7 @@ type Formatter interface { Format(*Entry) ([]byte, error) } -// This is to not silently overwrite `time`, `msg`, `method` and `level` fields when +// This is to not silently overwrite `time`, `msg`, `func` and `level` fields when // dumping it. If this code wasn't there doing: // // logrus.WithField("level", 1).Info("hello") @@ -44,10 +46,10 @@ func prefixFieldClashes(data Fields, reportCaller bool) { data["fields.level"] = l } - // If reportCaller is not set, 'method' will not conflict. + // If reportCaller is not set, 'func' will not conflict. if reportCaller { - if l, ok := data["method"]; ok { - data["fields.method"] = l + if l, ok := data["func"]; ok { + data["fields.func"] = l } } } diff --git a/json_formatter.go b/json_formatter.go index 110b91dd1..8bdac89ff 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -9,10 +9,10 @@ type fieldKey string type FieldMap map[fieldKey]string const ( - FieldKeyMsg = "msg" - FieldKeyLevel = "level" - FieldKeyTime = "time" - FieldKeyCaller = "method" + FieldKeyMsg = "msg" + FieldKeyLevel = "level" + FieldKeyTime = "time" + FieldKeyFunc = "func" ) func (f FieldMap) resolve(key fieldKey) string { @@ -34,10 +34,10 @@ type JSONFormatter struct { // As an example: // formatter := &JSONFormatter{ // FieldMap: FieldMap{ - // FieldKeyTime: "@timestamp", - // FieldKeyLevel: "@level", - // FieldKeyMsg: "@message", - // FieldKeyCaller: "@caller", + // FieldKeyTime: "@timestamp", + // FieldKeyLevel: "@level", + // FieldKeyMsg: "@message", + // FieldKeyFunc: "@caller", // }, // } FieldMap FieldMap @@ -69,7 +69,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() if entry.HasCaller() { - data[f.FieldMap.resolve(FieldKeyCaller)] = entry.Caller + data[f.FieldMap.resolve(FieldKeyFunc)] = entry.Caller } serialized, err := json.Marshal(data) if err != nil { diff --git a/json_formatter_test.go b/json_formatter_test.go index 4a7e5e7c4..84c788261 100644 --- a/json_formatter_test.go +++ b/json_formatter_test.go @@ -174,7 +174,7 @@ func TestFieldDoesNotClashWithCaller(t *testing.T) { SetReportCaller(false) formatter := &JSONFormatter{} - b, err := formatter.Format(WithField("method", "howdy pardner")) + b, err := formatter.Format(WithField("func", "howdy pardner")) if err != nil { t.Fatal("Unable to format entry: ", err) } @@ -185,15 +185,15 @@ func TestFieldDoesNotClashWithCaller(t *testing.T) { t.Fatal("Unable to unmarshal formatted entry: ", err) } - if entry["method"] != "howdy pardner" { - t.Fatal("method field replaced when ReportCaller=false") + if entry["func"] != "howdy pardner" { + t.Fatal("func field replaced when ReportCaller=false") } } func TestFieldClashWithCaller(t *testing.T) { SetReportCaller(true) formatter := &JSONFormatter{} - e := WithField("method", "howdy pardner") + e := WithField("func", "howdy pardner") e.Caller = "somefunc" b, err := formatter.Format(e) if err != nil { @@ -206,14 +206,14 @@ func TestFieldClashWithCaller(t *testing.T) { t.Fatal("Unable to unmarshal formatted entry: ", err) } - if entry["fields.method"] != "howdy pardner" { - t.Fatalf("fields.method not set to original method field when ReportCaller=true (got '%s')", - entry["fields.method"]) + if entry["fields.func"] != "howdy pardner" { + t.Fatalf("fields.func not set to original func field when ReportCaller=true (got '%s')", + entry["fields.func"]) } - if entry["method"] != "somefunc" { - t.Fatalf("method not set as expected when ReportCaller=true (got '%s')", - entry["method"]) + if entry["func"] != "somefunc" { + t.Fatalf("func not set as expected when ReportCaller=true (got '%s')", + entry["func"]) } SetReportCaller(false) // return to default value diff --git a/logrus_test.go b/logrus_test.go index 58d9981a2..7b5c2c4ee 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -57,7 +57,7 @@ func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields ma assertions(fields) } -// TestReportCaller verifies that when ReportCaller is set, the 'method' field +// TestReportCaller verifies that when ReportCaller is set, the 'func' field // is added, and when it is unset it is not set or modified func TestReportCaller(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { @@ -66,7 +66,7 @@ func TestReportCaller(t *testing.T) { }, func(fields Fields) { assert.Equal(t, "testNoCaller", fields["msg"]) assert.Equal(t, "info", fields["level"]) - assert.Equal(t, nil, fields["method"]) + assert.Equal(t, nil, fields["func"]) }) LogAndAssertJSON(t, func(log *Logger) { @@ -75,7 +75,7 @@ func TestReportCaller(t *testing.T) { }, func(fields Fields) { assert.Equal(t, "testWithCaller", fields["msg"]) assert.Equal(t, "info", fields["level"]) - assert.Equal(t, "testing.tRunner", fields["method"]) + assert.Equal(t, "testing.tRunner", fields["func"]) }) } @@ -279,36 +279,36 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) { err := json.Unmarshal(buffer.Bytes(), &fields) assert.NoError(t, err, "should have decoded first message") - assert.Equal(t, len(fields), 5, "should have msg/time/level/method/context fields") + assert.Equal(t, len(fields), 5, "should have msg/time/level/func/context fields") assert.Equal(t, "looks delicious", fields["msg"]) assert.Equal(t, "eating raw fish", fields["context"]) - assert.Equal(t, "testing.tRunner", fields["method"]) + assert.Equal(t, "testing.tRunner", fields["func"]) buffer.Reset() logger.WithFields(Fields{ - "foo": "a", + "Clyde": "Stubblefield", }).WithFields(Fields{ - "bar": "b", + "Jab'o": "Starks", }).WithFields(Fields{ - "baz": "c", + "uri": "https://www.youtube.com/watch?v=V5DTznu-9v0", }).WithFields(Fields{ - "method": "man", + "func": "y drummer", }).WithFields(Fields{ - "clan": "Wu Tang", - }).Print("omg it is!") + "James": "Brown", + }).Print("The hardest workin' man in show business") err = json.Unmarshal(buffer.Bytes(), &fields) assert.NoError(t, err, "should have decoded second message") assert.Equal(t, 10, len(fields), "should have all builtin fields plus foo,bar,baz,...") - assert.Equal(t, "omg it is!", fields["msg"]) - assert.Equal(t, "a", fields["foo"]) - assert.Equal(t, "b", fields["bar"]) - assert.Equal(t, "c", fields["baz"]) - assert.Equal(t, "man", fields["fields.method"]) - assert.Equal(t, "Wu Tang", fields["clan"]) + assert.Equal(t, "Stubblefield", fields["Clyde"]) + assert.Equal(t, "Starks", fields["Jab'o"]) + assert.Equal(t, "https://www.youtube.com/watch?v=V5DTznu-9v0", fields["uri"]) + assert.Equal(t, "y drummer", fields["fields.func"]) + assert.Equal(t, "Brown", fields["James"]) + assert.Equal(t, "The hardest workin' man in show business", fields["msg"]) assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry") - assert.Equal(t, "testing.tRunner", fields["method"]) + assert.Equal(t, "testing.tRunner", fields["func"]) logger.ReportCaller = false // return to default value } diff --git a/text_formatter.go b/text_formatter.go index b0fa789ed..b4abd50cd 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -89,7 +89,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { } f.appendKeyValue(b, "level", entry.Level.String()) if entry.HasCaller() { - f.appendKeyValue(b, "method", entry.Caller) + f.appendKeyValue(b, "func", entry.Caller) } if entry.Message != "" { f.appendKeyValue(b, "msg", entry.Message) From 5665f08805ad69e2c3dab95e2a540d4e097368b9 Mon Sep 17 00:00:00 2001 From: puddingfactory Date: Sat, 10 Dec 2016 15:34:44 -0600 Subject: [PATCH 185/547] Add Logentrus, hook for Logentries, to list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 05d678ad8..0e2257448 100644 --- a/README.md +++ b/README.md @@ -240,6 +240,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [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) | +| [Logentrus](https://github.com/puddingfactory/logentrus) | Hook for logging to [Logentries](https://logentries.com/) | #### Level logging From 5830d040b592a78c76a1610f88b05884bd96a959 Mon Sep 17 00:00:00 2001 From: at15 Date: Tue, 10 Jan 2017 21:43:36 -0800 Subject: [PATCH 186/547] Remove miniTS in TextFormatter - Related issues: https://github.com/sirupsen/logrus/issues/457 - miniTS use current time instead of time the log function is called, which is inaccurate when hook takes a long time - `miniTS` is removed and replaced by `int(entry.Time.Sub(baseTimestamp)/time.Second)` in `printColored` which is the only usage of `miniTS` --- text_formatter.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index 20f2d7edc..dd86caad1 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -28,10 +28,6 @@ func init() { isTerminal = IsTerminal() } -func miniTS() int { - return int(time.Since(baseTimestamp) / time.Second) -} - type TextFormatter struct { // Set to true to bypass checking for a TTY before outputting colors. ForceColors bool @@ -118,7 +114,7 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin if f.DisableTimestamp { fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s ", levelColor, levelText, entry.Message) } else if !f.FullTimestamp { - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), entry.Message) } else { fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message) } From 49dea0a6936a6436c2b81742efea2f8397fd9d4a Mon Sep 17 00:00:00 2001 From: Ben Gadbois Date: Wed, 11 Jan 2017 19:19:12 +0100 Subject: [PATCH 187/547] Small var declaration cleanup --- text_formatter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text_formatter.go b/text_formatter.go index 9114b3ca4..3fcd7f06f 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -58,7 +58,7 @@ type TextFormatter struct { func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { var b *bytes.Buffer - var keys []string = make([]string, 0, len(entry.Data)) + keys := make([]string, 0, len(entry.Data)) for k := range entry.Data { keys = append(keys, k) } From 1d1af23bcfe389024e5e0ca9622f53e680419a2c Mon Sep 17 00:00:00 2001 From: Tony Lee Date: Tue, 24 Jan 2017 23:05:25 +1100 Subject: [PATCH 188/547] Add DisableLevelTruncation description to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 206c746cc..7622d31b4 100644 --- a/README.md +++ b/README.md @@ -318,6 +318,8 @@ The built-in logging formatters are: * *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` + * When colors are enabled, levels are truncated to 4 characters by default. To disable + truncation set the `DisableLevelTruncation` field to `true`. * `logrus.JSONFormatter`. Logs fields as JSON. Third party logging formatters: From 4fe49fcbe36c17a8b767494c58e1f3b1ac4f7ef0 Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Fri, 3 Feb 2017 15:08:53 -0800 Subject: [PATCH 189/547] add caller logic to DisableTimestamp case --- README.md | 2 +- text_formatter.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b6e2afdba..720f85afa 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ This adds the caller as 'method' like so: ```text time="2015-03-26T01:27:38-04:00" level=fatal method=arcticcreatures.migrate msg="a penguin swims by" animal=penguin ``` -Note that this does add measurable overhead - the cost will depend on the of Go, but is +Note that this does add measurable overhead - the cost will depend on the version of Go, but is between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your environment via benchmarks: ``` diff --git a/text_formatter.go b/text_formatter.go index 6f9c72d9e..7c63ac310 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -121,7 +121,7 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin } if f.DisableTimestamp { - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s ", levelColor, levelText, entry.Message) + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, entry.Message) } else if !f.FullTimestamp { fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message) } else { From 43c7feef3c2e4f3ae601c3de4e2d3b61adc22eb1 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Sun, 5 Feb 2017 09:21:03 -0500 Subject: [PATCH 190/547] 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 1960169ef..632ecbec1 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 329038f6c..190297abf 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 a3c6f6e7d..943394c28 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 3727e8adf..67be1c0fc 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 076de5dae..f75e13e36 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 cfdced1fa0b48d477c84801a66b5d20d8ec7b8ae Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Sun, 5 Feb 2017 19:10:19 -0500 Subject: [PATCH 191/547] 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 a1623ec00..ea7e5510f 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 c6d290c77..d9481589f 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 f75e13e36..b4dffa1e0 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 435d6a7699cb08b7ff1ec9bb6e1c5ec20dc07488 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 6 Feb 2017 19:16:20 -0500 Subject: [PATCH 192/547] 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 206c746cc..3823323d0 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 ea7e5510f..ad703fcb6 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 943394c28..f3d6f96b5 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 4bd128b326731ce8473a667232c90ec30fbd6c55 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 6 Feb 2017 19:40:31 -0500 Subject: [PATCH 193/547] readme: add some entries from stale PRs --- README.md | 51 ++++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 3823323d0..eaaea7977 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. | +| [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) | | [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. | +| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic | +| [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) | +| [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. | -| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) | -| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. | +| [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` | -| [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/) | +| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka | | [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 | +| [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 | -| [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 | +| [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 | -| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic | +| [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) | -| [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/) | -| [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/)| +| [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)| -| [Logstash](https://github.com/bshuster-repo/logrus-logstash-hook) | Hook for logging to [Logstash](https://www.elastic.co/products/logstash) | +| [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 | -| [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) | -| [Logentrus](https://github.com/puddingfactory/logentrus) | Hook for logging to [Logentries](https://logentries.com/) | - #### Level logging From c6279d3faf6013ac443a64f17bffa2e25802037e Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 6 Feb 2017 19:40:39 -0500 Subject: [PATCH 194/547] 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 f2c2bc211..47a6c4b7f 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 44950370446224dd27e0a7dde00cd5968cbd7e31 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 6 Feb 2017 19:44:51 -0500 Subject: [PATCH 195/547] readme: even more hooks --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index eaaea7977..77941d8ac 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 3593278cf50ef0fdd32e77015fa649590895f4d1 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 6 Feb 2017 19:51:07 -0500 Subject: [PATCH 196/547] 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 77941d8ac..a404823ea 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 ab9932f78ea3e6d9c759e229d0537bc851930bca Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 6 Feb 2017 19:56:22 -0500 Subject: [PATCH 197/547] readme: add section on overriding stdlib logger --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index a404823ea..067146499 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 4558abb788313423ff5cb88de668fc987d807d95 Mon Sep 17 00:00:00 2001 From: Majid Arif Siddiqui Date: Tue, 7 Feb 2017 11:28:14 +0800 Subject: [PATCH 198/547] 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 67be1c0fc..97ad4e01b 100644 --- a/terminal_windows.go +++ b/terminal_windows.go @@ -8,6 +8,8 @@ package logrus import ( + "io" + "os" "syscall" "unsafe" ) From 251f5c0af0b982fe58283140e38d17a7055b41c5 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 7 Feb 2017 06:40:37 -0500 Subject: [PATCH 199/547] 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 067146499..361b333ac 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 e740bf8c3b1004a8fe4aa14c441ce3553bc924ec Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 7 Feb 2017 06:49:02 -0500 Subject: [PATCH 200/547] changelog: add fixing windows terminal detection --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47a6c4b7f..6cee3c996 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 944f90eeb4ffdee79ecc5eef2e4b9698c493a6cc Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 7 Feb 2017 13:34:08 -0500 Subject: [PATCH 201/547] readme: add section on default fields --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 361b333ac..640cf61f6 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 d23cc856ad78bb93896dec6aeadc4a811dd5c689 Mon Sep 17 00:00:00 2001 From: "Aaron.L.Xu" Date: Wed, 8 Feb 2017 19:18:14 +0800 Subject: [PATCH 202/547] 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 b4c9e8475..8af90637a 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 97315faf598cca325739c15028d873833de5127a Mon Sep 17 00:00:00 2001 From: Darren McCleary Date: Wed, 8 Feb 2017 14:03:21 -0500 Subject: [PATCH 203/547] Fixed import --- terminal_appengine.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/terminal_appengine.go b/terminal_appengine.go index 632ecbec1..e011a8694 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 13110eeae1cae7b49ce0f8250c91b5ae77427bbc 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 204/547] Fixes typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 640cf61f6..05915d003 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 46efd14bb47495cecdea8bc62a7e111cc6ca0378 Mon Sep 17 00:00:00 2001 From: Darren McCleary Date: Mon, 13 Feb 2017 10:56:35 -0500 Subject: [PATCH 205/547] 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 f3d6f96b5..62096dbac 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 97ad4e01b..05d2f91f1 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 107703fa6..879ebd242 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 533bdf7b3a9289b0306cfb2644a94f5fedcf3cc7 Mon Sep 17 00:00:00 2001 From: Ben Brooks Date: Tue, 14 Feb 2017 10:53:03 +0000 Subject: [PATCH 206/547] 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 b4dffa1e0..51397ccc4 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 107703fa6..54fc8fe84 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 b0ef3b5929de55585defcb729789fcd5dc858ed5 Mon Sep 17 00:00:00 2001 From: Ben Brooks Date: Tue, 14 Feb 2017 11:51:23 +0000 Subject: [PATCH 207/547] 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 51397ccc4..ef799f11b 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 54fc8fe84..fce4cfe39 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 2858674184a8ddf915d3fa8f28c6d0d72c5538f7 Mon Sep 17 00:00:00 2001 From: Mohan Raj Rajamanickam Date: Tue, 14 Feb 2017 16:37:12 -0800 Subject: [PATCH 208/547] 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 640cf61f6..5c42e3ad0 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 c80fe22a100028a955bb580a3fcc37e3bc37b7a3 Mon Sep 17 00:00:00 2001 From: Ben Brooks Date: Wed, 15 Feb 2017 13:08:26 +0000 Subject: [PATCH 209/547] 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 ef799f11b..ba8885406 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 fce4cfe39..9793b5f37 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 c69cf991e5fd8468985bbcb8745f8be61db2c482 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Wed, 15 Feb 2017 11:43:24 -0500 Subject: [PATCH 210/547] changelog: add entries for formatter --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cee3c996..a6c83d783 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 90d8ad9d228b1d4b7cdc0c3b81b34328baaab84e Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Fri, 17 Feb 2017 14:09:07 +0000 Subject: [PATCH 211/547] 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 640cf61f6..1641d4e7f 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 815199042504fb48b6484f248a424029abd99d93 Mon Sep 17 00:00:00 2001 From: Denis Barakhtanov Date: Fri, 24 Feb 2017 10:16:21 +1100 Subject: [PATCH 212/547] 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 f3d6f96b5..3c86b1abe 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 1e8057171b1a3751eb09d85086dacde803545e4c 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 213/547] add import for example --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 640cf61f6..65317138d 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 bdaae33b727749c55826db67b4d7325d3dda70ab Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 27 Feb 2017 07:07:31 -0500 Subject: [PATCH 214/547] changelog: add 481 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6c83d783..e22b05be2 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 25eef2c6c69cf4cb61d25816bca7f7d16ae852ad Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 27 Feb 2017 07:07:50 -0500 Subject: [PATCH 215/547] release 0.11.3 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e22b05be2..7b85e6946 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 7dab7bad63832aed71d6a5740628d85742692662 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 27 Feb 2017 07:44:09 -0500 Subject: [PATCH 216/547] changelog: 0.11.4 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b85e6946..4c9418f8d 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 4b587215f0dc83a690e2a26c7e86a8c38bdcbb86 Mon Sep 17 00:00:00 2001 From: "Beau N. Brewer" Date: Wed, 1 Mar 2017 12:26:05 -0700 Subject: [PATCH 217/547] 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 640cf61f6..34df95595 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 5f42f8f4bb6c46afdebbd61b072bca5e0cc4b730 Mon Sep 17 00:00:00 2001 From: David Glasser Date: Mon, 20 Jun 2016 15:55:49 -0700 Subject: [PATCH 218/547] 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 bfc478055..78cbc2825 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 f74d2aa5f..7bdebedc6 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 fd94383093d080670b3fa687a99e1e205e36cbdc Mon Sep 17 00:00:00 2001 From: Ivan Borshukov Date: Tue, 7 Mar 2017 18:02:20 +0200 Subject: [PATCH 219/547] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 640cf61f6..93ed0407b 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 737ca0b1c15ddf8f094da9790c39ee24193d0cca Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Wed, 8 Mar 2017 15:37:04 -0500 Subject: [PATCH 220/547] changelog for #372 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c9418f8d..cfec21db2 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 57ea01721354580e04b9560fd5b46ee26b798109 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 14 Mar 2017 15:23:53 -0400 Subject: [PATCH 221/547] release 0.11.5 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfec21db2..747e4d89a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# Unreleased +# 0.11.5 * feature: add writer and writerlevel to entry (#372) From 687a6801e472e68316e584f37798af0cfd0ec2b9 Mon Sep 17 00:00:00 2001 From: Alexey Remizov Date: Wed, 22 Mar 2017 17:09:05 +0300 Subject: [PATCH 222/547] 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 266554e9f..753d6a2e6 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 68bcd63b22bc56198121cf0eb2534f1871d0fd52 Mon Sep 17 00:00:00 2001 From: Paul Litvak Date: Wed, 22 Mar 2017 22:32:29 +0200 Subject: [PATCH 223/547] Added SQS Hook --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c32287611..0a545f075 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 eaefde24ca94638f9b99c9dd42aa65d5b5a92932 Mon Sep 17 00:00:00 2001 From: DmitriyMV Date: Thu, 23 Mar 2017 19:13:19 +0300 Subject: [PATCH 224/547] 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 4edbe7a2d..320e5d5b8 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 9a0120ac1..1aeaa90ba 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 b769f3d35..370fff5d1 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 e59669111..dd3899974 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 68bd005ecf5e5a1db55e1999f9edb647b390034f Mon Sep 17 00:00:00 2001 From: Jay Qin Date: Thu, 23 Mar 2017 09:50:46 -0700 Subject: [PATCH 225/547] 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 266554e9f..753d6a2e6 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 588442c2206c73309bf37c0d0e16acbbc267f4d0 Mon Sep 17 00:00:00 2001 From: Jonathan Hall Date: Thu, 20 Apr 2017 09:29:15 +0200 Subject: [PATCH 226/547] 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 068812535..0190ba003 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 891ec208dfb3a2fe9820a8bcc300f18210a278e1 Mon Sep 17 00:00:00 2001 From: Jonathan Hall Date: Thu, 20 Apr 2017 09:33:10 +0200 Subject: [PATCH 227/547] 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 0190ba003..78d75ff45 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 332dc7b7edb62dd23327e27332746ccf2b01d911 Mon Sep 17 00:00:00 2001 From: Jonathan Hall Date: Thu, 20 Apr 2017 09:37:32 +0200 Subject: [PATCH 228/547] 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 78d75ff45..a94a6f95d 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 a55df01b3c6731ab414bd28e0f376505c8c88cce Mon Sep 17 00:00:00 2001 From: Jonathan Hall Date: Thu, 20 Apr 2017 09:50:47 +0200 Subject: [PATCH 229/547] 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 804c56943..ef00c00e2 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 5b39da1dbd509d2e308f42cd2e737710dad92882 Mon Sep 17 00:00:00 2001 From: Urjit Singh Bhatia Date: Tue, 2 May 2017 10:47:34 -0700 Subject: [PATCH 230/547] 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 c32287611..0572d7a6d 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 c4c644e424e7bc0e641d327cdb4c56b9a87e0675 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Wed, 3 May 2017 17:23:24 +0100 Subject: [PATCH 231/547] 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 c32287611..2d7b14df1 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 18894d81fc85b629b536ca0df457ad873d06aecf Mon Sep 17 00:00:00 2001 From: Mattias Lundell Date: Fri, 5 May 2017 14:18:10 +0200 Subject: [PATCH 232/547] 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 266554e9f..753d6a2e6 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 c3259aff6b969c830c2f5f9e4c8ec5c3de3df69a Mon Sep 17 00:00:00 2001 From: JodeZer Date: Thu, 11 May 2017 18:19:16 +0800 Subject: [PATCH 233/547] 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 b6195df1e..65eec9cf1 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 37203d51f250204abb8c11287e6192b78d825684 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Fri, 12 May 2017 12:12:49 +0100 Subject: [PATCH 234/547] 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 05d2f91f1..887185776 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 0bfa21105b29be03b548a4caba2e97469a20d435 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Fri, 12 May 2017 14:51:38 -0400 Subject: [PATCH 235/547] 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 65eec9cf1..08dd46862 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 000000000..01e9abc4a --- /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 84459f19d2523acff3489a373e1be701b96e2abf Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Fri, 12 May 2017 15:09:01 -0400 Subject: [PATCH 236/547] readme: add note on casing --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 08dd46862..b3c75cfd3 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 9ea5178b634cf3f9aa0eed237d04983311a8ef85 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Fri, 12 May 2017 15:15:51 -0400 Subject: [PATCH 237/547] 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 b3c75cfd3..46dda8594 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 000000000..10e841338 --- /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 6383c94b273327fe43b6cb46da55d16ef3dd72e6 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Fri, 12 May 2017 15:21:58 -0400 Subject: [PATCH 238/547] 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 dddd5f877..da67aba06 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 ad703fcb6..3e112b4ee 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 3187f6d3e..c8470c387 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 066704b37..b9c509b7b 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 a94a6f95d..4eabccffc 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 753d6a2e6..e787ea175 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 d86d00936c2753b67fbf29e6c2d87de516aec8b0 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Fri, 12 May 2017 22:28:26 +0100 Subject: [PATCH 239/547] 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 887185776..7a336307e 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 ee196c316162946d6a5e404f3691dacf01454a6b Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 15 May 2017 06:31:03 -0400 Subject: [PATCH 240/547] travis: don't go get --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ef00c00e2..db83e5a49 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 66c160cc5564c4dbc81dd3d672297db0571f6ed2 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 15 May 2017 06:34:03 -0400 Subject: [PATCH 241/547] travis: don't go get recursively --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index db83e5a49..a8a5240f1 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 1fdc90f9e374addddf46f1d6dd671afa136bd309 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 15 May 2017 06:45:16 -0400 Subject: [PATCH 242/547] 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 a8a5240f1..924f3c46b 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 b761d2fae..2674b8bd5 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 022b77830..d18296348 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 a5e3366df26c249c3fb653142f00aae9337dadc1 Mon Sep 17 00:00:00 2001 From: Shulhan Date: Thu, 18 May 2017 17:03:42 +0700 Subject: [PATCH 243/547] README: add hook for Mattermost --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2674b8bd5..6ea96f307 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 a5be4b8bd5d7e6ef579927f8475c74f1b4631555 Mon Sep 17 00:00:00 2001 From: Tomas Doran Date: Fri, 19 May 2017 13:00:16 +0100 Subject: [PATCH 244/547] 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 b9c509b7b..92b391c17 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 a36e20032..204f0016d 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 42762dc10..8d7fbe452 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 4eabccffc..62c4845df 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 d69455ba0..3f55cfe31 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 ca4be237a5481c10520d931a4a84e528c09be2fd Mon Sep 17 00:00:00 2001 From: Etourneau Gwenn Date: Thu, 25 May 2017 09:53:41 +0900 Subject: [PATCH 245/547] Updating Readme to add syslogtls repo. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2674b8bd5..725b10b18 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 2111c5da61a151859559e292d292d6dbe44f4a66 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 6 Jun 2017 15:05:30 -0400 Subject: [PATCH 246/547] readme: more explicit case instructions --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2674b8bd5..de7bff07f 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 6ceffbb32bf2d117a0d2f7531f0ef9f1cbc49c12 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 6 Jun 2017 16:59:16 -0400 Subject: [PATCH 247/547] 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 9b4994a45..cbe8b6962 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 2f760a8272217f06377a3b9648867c97410a0d41 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 6 Jun 2017 16:59:45 -0400 Subject: [PATCH 248/547] 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 01e9abc4a..000000000 --- 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 10e841338..000000000 --- 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 6f09107969ec1819011b5a43444af9650e96e8cd Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 8 Jun 2017 17:02:02 -0400 Subject: [PATCH 249/547] changelog: bump to v1.0.0 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 747e4d89a..63d415e12 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 8a89ea11447e9b1fc992bb7bacb8127fcbd09bb6 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 8 Jun 2017 18:14:41 -0400 Subject: [PATCH 250/547] ci: v1.0.0 fix travis --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 924f3c46b..ceace13bb 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 439e9aa2600e039ab8be277504839c7e68c85547 Mon Sep 17 00:00:00 2001 From: Alex Haynes Date: Tue, 13 Jun 2017 13:28:47 -0500 Subject: [PATCH 251/547] 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 cbe8b6962..ed03178bc 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 8b1d7dfb90ef7b2456a0d60964bc3e8cf1947a8b Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 18 Jun 2017 09:48:52 -0400 Subject: [PATCH 252/547] 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 ba8885406..6821fcbd9 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 9793b5f37..08571db90 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 19fbc5c2882803643bf2c6be8c85a8580dc17e95 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 20 Jun 2017 10:45:10 -0400 Subject: [PATCH 253/547] 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 cbe8b6962..3f324e21b 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 f44d21fd7356264a6c9145d6aabac74146a61d41 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 29 Jun 2017 20:54:20 -0400 Subject: [PATCH 254/547] readme: clarify casing issues further --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3f324e21b..7941ebdb8 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 307e4b9fb656a44506d19c4ddee9a8332ac1d1bc Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Thu, 6 Jul 2017 09:44:07 -0400 Subject: [PATCH 255/547] readme: add note about lookign for maintainers --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 7941ebdb8..f42b65230 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 7145527fd182e85c65df6bb15f3346d63b9cdf58 Mon Sep 17 00:00:00 2001 From: Andrew Pennebaker Date: Thu, 6 Jul 2017 17:21:34 -0500 Subject: [PATCH 256/547] 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 f42b65230..018e915cd 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 3866113e3fcee3cf20df1c255610c6d9838dd4d6 Mon Sep 17 00:00:00 2001 From: Steve Jarvis Date: Fri, 7 Jul 2017 16:29:37 -0400 Subject: [PATCH 257/547] 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 1aeaa90ba..013183eda 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 370fff5d1..b44966f97 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 0196a647f872a197e51b495df7b4a3078aaf05af Mon Sep 17 00:00:00 2001 From: kpcyrd Date: Mon, 24 Apr 2017 19:27:50 +0200 Subject: [PATCH 258/547] 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 ba8885406..8006c0d48 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 3f721380b7ee9828e56b8a18befc2ce06a2d4e1f Mon Sep 17 00:00:00 2001 From: Paul Seiffert Date: Mon, 10 Jul 2017 14:09:37 +0200 Subject: [PATCH 259/547] 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 8006c0d48..32adbf8ff 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 9793b5f37..f02dc0c88 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 fd7ef48af1b45a9ee7a4cd1e8fa2af1e0dd506ee Mon Sep 17 00:00:00 2001 From: Paul Seiffert Date: Mon, 10 Jul 2017 14:17:59 +0200 Subject: [PATCH 260/547] 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 32adbf8ff..26dcc1553 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 6e8d69b78e54b06edad27cd2a00fbdc4837c7c39 Mon Sep 17 00:00:00 2001 From: Paul Seiffert Date: Mon, 10 Jul 2017 17:40:03 +0200 Subject: [PATCH 261/547] Mention bugfix in changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63d415e12..633632254 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 5fd2a2e66cc7905db74131237d4e2270102298c5 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Mon, 10 Jul 2017 10:32:56 -0400 Subject: [PATCH 262/547] 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 e47935208..82aeb4eef 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 6f4963d9d0887d5649b626231108d588d3fdcc24 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Wed, 12 Jul 2017 14:33:57 +0200 Subject: [PATCH 263/547] 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 dc4911f65..1fc55aee6 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 0a0e9bee6e777f6fad6914f3c7bc38dec0b36eb2 Mon Sep 17 00:00:00 2001 From: Paul Seiffert Date: Wed, 12 Jul 2017 17:15:13 +0200 Subject: [PATCH 264/547] 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 e12501501..6f573a2fd 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 1fc55aee6..93f47b7b9 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 db5357eb77fb40000cb47a2109e441cca04de0d7 Mon Sep 17 00:00:00 2001 From: Paul Seiffert Date: Wed, 12 Jul 2017 17:16:13 +0200 Subject: [PATCH 265/547] 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 6f573a2fd..6fe24fcfa 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 e5b174c09c97122ccf3fc7fdef2c87988aa71fb1 Mon Sep 17 00:00:00 2001 From: Paul Seiffert Date: Wed, 12 Jul 2017 17:29:56 +0200 Subject: [PATCH 266/547] 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 93f47b7b9..d7b3bcb14 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 9542241a6b2cee02970504a593667a1d44bf233e Mon Sep 17 00:00:00 2001 From: Paul Seiffert Date: Wed, 12 Jul 2017 17:33:04 +0200 Subject: [PATCH 267/547] 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 6fe24fcfa..6aa48cfb6 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 58fe16602405f0a70daefd7178257e3d7e4866bb Mon Sep 17 00:00:00 2001 From: Paul Seiffert Date: Wed, 12 Jul 2017 18:16:53 +0200 Subject: [PATCH 268/547] Mention quoting bugfix in CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 633632254..03fe4b15f 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 94693cd83b1b6920e2c2dfa00d067ae7152da6fa Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Thu, 13 Jul 2017 13:42:50 +0200 Subject: [PATCH 269/547] add changelog entry about making SetLevel public --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03fe4b15f..c443aed09 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 5ba8a60a7e24085eb88434185fe76a1dc62cc1b1 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Thu, 13 Jul 2017 13:46:35 +0200 Subject: [PATCH 270/547] 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 6aa48cfb6..cf3f17f2f 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 d7b3bcb14..ecb8f1271 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 da98aeea92556bb7378703dfede73de2191297dd Mon Sep 17 00:00:00 2001 From: DmitriyMV Date: Fri, 14 Jul 2017 14:59:01 +0300 Subject: [PATCH 271/547] Improve logrus.Entry.Level documentation --- entry.go | 1 + 1 file changed, 1 insertion(+) diff --git a/entry.go b/entry.go index 320e5d5b8..5bf582ef2 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 25decb9f12dbe333676f7fca81cc2c7cad31caed Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Tue, 18 Jul 2017 15:18:46 +0200 Subject: [PATCH 272/547] 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 92b391c17..1bbc0f72d 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 204f0016d..329ce0d60 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 8d7fbe452..5ec3a4445 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 3641b5ce9c6348b94078358505e850bb26239acf Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Wed, 19 Jul 2017 16:24:10 +0200 Subject: [PATCH 273/547] 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 82aeb4eef..7622d016c 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 5bbc9e49e089b1d037a6daf43e5f13a7bbdaac0b Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Wed, 19 Jul 2017 16:31:49 +0200 Subject: [PATCH 274/547] test all subpackages too --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ceace13bb..c6e1e44c4 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 5aee82a69a409689f07f1fc2ca54cfb4f3f7efee Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Wed, 19 Jul 2017 16:53:07 +0200 Subject: [PATCH 275/547] travis needs the airbrake hook --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c6e1e44c4..69eb5a408 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 f1980186628b8b53aa926f9402fb21cbde14a5fa Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Wed, 19 Jul 2017 17:47:53 +0200 Subject: [PATCH 276/547] 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 b44966f97..2acab0509 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 f84d86ba218de7ff8086f200b19a0d9312403f3b Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Fri, 21 Jul 2017 16:06:23 +0200 Subject: [PATCH 277/547] 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 ecb8f1271..7f7b78c2c 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 b7eac6ef31fe2ec7ec45867dfa4d2871721dcbe0 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Fri, 21 Jul 2017 16:14:28 +0200 Subject: [PATCH 278/547] 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 cf3f17f2f..ec8d47356 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 7f7b78c2c..d93b931e5 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 ae6ee5020afcb30ba17b651bd0b247d8a83d7ba8 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Tue, 25 Jul 2017 09:39:35 +0200 Subject: [PATCH 279/547] 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 ec8d47356..79239cf14 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 c53b3c1128f0034e398f3e18992f8c277184b404 Mon Sep 17 00:00:00 2001 From: Huang Huang Date: Tue, 25 Jul 2017 22:52:40 +0800 Subject: [PATCH 280/547] readme: remove duplicate godoc link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7622d016c..4f5ce576d 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 e6cff01b39503de73c2e328fd260e4644324c012 Mon Sep 17 00:00:00 2001 From: Davide D'Agostino Date: Tue, 25 Jul 2017 14:20:14 -0700 Subject: [PATCH 281/547] 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 190297abf..c3b74030c 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 51da457cc4b4cab0bd538d8d73aa837f58f10f5a Mon Sep 17 00:00:00 2001 From: Christy Perez Date: Tue, 30 May 2017 15:37:58 -0400 Subject: [PATCH 282/547] 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 308160ca8..c13a944fa 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 5ef60112ac947ac072af1140476956209070fa29 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Tue, 25 Jul 2017 10:26:58 +0200 Subject: [PATCH 283/547] 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 69eb5a408..28a702af7 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 90dde0053684ffa28b5ed2863ffdbe77739f90e0 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Tue, 25 Jul 2017 10:29:46 +0200 Subject: [PATCH 284/547] 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 c13a944fa..88d7298e2 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 69a4d0e239bd8cc24f4616e347deceebddcae68f Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Wed, 26 Jul 2017 10:15:24 +0200 Subject: [PATCH 285/547] 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 28a702af7..a23296a53 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 5f6be4d3c..d7b3893f3 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 190297abf..198175b4e 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 7a336307e..db9a8fb51 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 fefb8cf75f5060d01b6a8a622f46d84905411416 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Wed, 26 Jul 2017 14:26:30 +0200 Subject: [PATCH 286/547] 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 b5fbe934d..b183ff5b1 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 e787ea175..fb01c1b10 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 79239cf14..362fa2872 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 bcfca4455e35c1b1959c17553d61583f2c7dc7b4 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 287/547] 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 000000000..96c2ce15f --- /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 0cd64e02c2f256e79623384238681df694c124dd Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Thu, 27 Jul 2017 14:06:49 +0200 Subject: [PATCH 288/547] 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 d18296348..71ed820da 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 d3c81a8ea34bd41e06db2d00029c7a0bc6e52384 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Thu, 27 Jul 2017 14:46:09 +0200 Subject: [PATCH 289/547] 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 71ed820da..a08b1a898 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 b0300f022051c906704ba46bb251170d8bd22bb3 Mon Sep 17 00:00:00 2001 From: dmathieu <42@dmathieu.com> Date: Thu, 27 Jul 2017 14:23:27 +0200 Subject: [PATCH 290/547] 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 e011a8694..000000000 --- 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 9a4611e7c..000000000 --- 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 3c86b1abe..000000000 --- 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 db9a8fb51..000000000 --- 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 362fa2872..be412aa94 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 9d1d8a4719b2099664538b361c1d9c1f6a24ab33 Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Wed, 2 Aug 2017 17:21:18 -0700 Subject: [PATCH 291/547] test updates - add tests for callers accessed directly or via function pointer (results are unchanged) - undo unwanted capitalization/export in previous commit --- entry.go | 8 ++++---- formatter.go | 4 ++-- logrus_test.go | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 54 insertions(+), 7 deletions(-) diff --git a/entry.go b/entry.go index 11f79c992..1d0a603fb 100644 --- a/entry.go +++ b/entry.go @@ -13,7 +13,7 @@ import ( var bufferPool *sync.Pool // qualified package name, cached at first use -var logrusPackage string +var LogrusPackage string // Positions in the call stack when tracing to report the calling method var minimumCallerDepth int @@ -126,8 +126,8 @@ func getCaller() (method string) { depth := runtime.Callers(minimumCallerDepth, pcs) // cache this package's fully-qualified name - if logrusPackage == "" { - logrusPackage = getPackageName(runtime.FuncForPC(pcs[0]).Name()) + if LogrusPackage == "" { + LogrusPackage = getPackageName(runtime.FuncForPC(pcs[0]).Name()) // now that we have the cache, we can skip a minimum count of known-logrus functions minimumCallerDepth = knownLogrusFrames @@ -138,7 +138,7 @@ func getCaller() (method string) { pkg := getPackageName(fullFuncName) // If the caller isn't part of this package, we're done - if pkg != logrusPackage { + if pkg != LogrusPackage { return fullFuncName } } diff --git a/formatter.go b/formatter.go index dc6631deb..ba7b509f1 100644 --- a/formatter.go +++ b/formatter.go @@ -2,8 +2,8 @@ package logrus import "time" -// DefaultTimestampFormat is YYYY-mm-DDTHH:MM:SS-TZ -const DefaultTimestampFormat = time.RFC3339 +// defaultTimestampFormat is YYYY-mm-DDTHH:MM:SS-TZ +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/logrus_test.go b/logrus_test.go index e724cc82b..66c863dd8 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -59,7 +59,9 @@ func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields ma // TestReportCaller verifies that when ReportCaller is set, the 'func' field // is added, and when it is unset it is not set or modified -func TestReportCaller(t *testing.T) { +// Verify that functions within the Logrus package aren't considered when +// discovering the caller. +func TestReportCallerWhenConfigured(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.ReportCaller = false log.Print("testNoCaller") @@ -79,6 +81,51 @@ func TestReportCaller(t *testing.T) { }) } +func logSomething(t *testing.T, message string) Fields { + var buffer bytes.Buffer + var fields Fields + + logger := New() + logger.Out = &buffer + logger.Formatter = new(JSONFormatter) + logger.ReportCaller = true + + // override the filter to allow reporting of functions within the logrus package + LogrusPackage = "bogusForTesting" + + entry := logger.WithFields(Fields{ + "foo": "bar", + }) + + entry.Info(message) + + err := json.Unmarshal(buffer.Bytes(), &fields) + assert.Nil(t, err) + + // now clear the override so as not to mess with other usage + LogrusPackage = "" + return fields +} + +// TestReportCallerHelperDirect - verify reference when logging from a regular function +func TestReportCallerHelperDirect(t *testing.T) { + fields := logSomething(t, "direct") + + assert.Equal(t, "direct", fields["msg"]) + assert.Equal(t, "info", fields["level"]) + assert.Equal(t, "github.com/dclendenan/logrus.logSomething", fields["func"]) +} + +// TestReportCallerHelperDirect - verify reference when logging from a function called via pointer +func TestReportCallerHelperViaPointer(t *testing.T) { + fptr := logSomething + fields := fptr(t, "via pointer") + + assert.Equal(t, "via pointer", fields["msg"]) + assert.Equal(t, "info", fields["level"]) + assert.Equal(t, "github.com/dclendenan/logrus.logSomething", fields["func"]) +} + func TestPrint(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.Print("test") From 3e15805e7733234c1b617f66dba637e7f81641c2 Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Wed, 2 Aug 2017 17:28:13 -0700 Subject: [PATCH 292/547] regex assertion rather than literal, for github path --- logrus_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logrus_test.go b/logrus_test.go index 66c863dd8..3fc68c9bf 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -113,7 +113,7 @@ func TestReportCallerHelperDirect(t *testing.T) { assert.Equal(t, "direct", fields["msg"]) assert.Equal(t, "info", fields["level"]) - assert.Equal(t, "github.com/dclendenan/logrus.logSomething", fields["func"]) + assert.Regexp(t, "github.com/.*/logrus.logSomething", fields["func"]) } // TestReportCallerHelperDirect - verify reference when logging from a function called via pointer @@ -123,7 +123,7 @@ func TestReportCallerHelperViaPointer(t *testing.T) { assert.Equal(t, "via pointer", fields["msg"]) assert.Equal(t, "info", fields["level"]) - assert.Equal(t, "github.com/dclendenan/logrus.logSomething", fields["func"]) + assert.Regexp(t, "github.com/.*/logrus.logSomething", fields["func"]) } func TestPrint(t *testing.T) { From 5f959dd42379868d1bf920673ecf429993ba2dfd Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Wed, 2 Aug 2017 17:33:13 -0700 Subject: [PATCH 293/547] add github path to log message in readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3c2236088..7a78d9855 100644 --- a/README.md +++ b/README.md @@ -73,12 +73,12 @@ log.SetReportCaller(true) This adds the caller as 'method' like so: ```json -{"animal":"penguin","level":"fatal","method":"arcticcreatures.migrate","msg":"a penguin swims by", +{"animal":"penguin","level":"fatal","method":"github.com/sirupsen/arcticcreatures.migrate","msg":"a penguin swims by", "time":"2014-03-10 19:57:38.562543129 -0400 EDT"} ``` ```text -time="2015-03-26T01:27:38-04:00" level=fatal method=arcticcreatures.migrate msg="a penguin swims by" animal=penguin +time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcreatures.migrate msg="a penguin swims by" animal=penguin ``` Note that this does add measurable overhead - the cost will depend on the version of Go, but is between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your From 4bd5ca387017a47f851d486c9e955b4cf0acfa70 Mon Sep 17 00:00:00 2001 From: Dave Clendenan Date: Wed, 2 Aug 2017 17:35:14 -0700 Subject: [PATCH 294/547] MD formatting --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7a78d9855..df7c0dacd 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,8 @@ To ensure this behaviour even if a TTY is attached, set your formatter as follow }) ``` +#### Logging Method Name + If you wish to add the calling method as a field, instruct the logger via: ```go log.SetReportCaller(true) From 0cbf577cb71fa3c6949e3a2e93e56b9b68c01d1f Mon Sep 17 00:00:00 2001 From: Ross McDonald Date: Mon, 7 Aug 2017 16:36:23 -0500 Subject: [PATCH 295/547] Add Telegram hook to README.md. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4f5ce576d..c9ff70e49 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 92c9f865cc563a96c4c17a8647ad2591da8ce217 Mon Sep 17 00:00:00 2001 From: Rafe Colton Date: Thu, 10 Aug 2017 10:46:39 -0400 Subject: [PATCH 296/547] 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 000000000..a2acf550c --- /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 000000000..d4ddffca3 --- /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 3e112b4ee..000000000 --- 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 c8470c387..000000000 --- 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 9393dd825ae3d4430d1050dd10de35e9be55cc12 Mon Sep 17 00:00:00 2001 From: Paul Seiffert Date: Tue, 15 Aug 2017 22:20:55 +0200 Subject: [PATCH 297/547] changelog: bump to v1.0.3 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c443aed09..8236d8b6e 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 f0aa914dfa158833676f4c6a6ab7d96448438147 Mon Sep 17 00:00:00 2001 From: Tracer Tong Date: Thu, 17 Aug 2017 16:33:45 +0900 Subject: [PATCH 298/547] 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 4f5ce576d..e69838608 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 704103e6f0f6c86776fce6fe89be2ab8898143f7 Mon Sep 17 00:00:00 2001 From: Aditya Mukerjee Date: Fri, 4 Aug 2017 11:24:22 -0400 Subject: [PATCH 299/547] Add AddHook method for logger --- logger.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/logger.go b/logger.go index 2acab0509..fdaf8a653 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 779e568272d4f3179a072de1c0a3dab3e434e54d Mon Sep 17 00:00:00 2001 From: Aditya Mukerjee Date: Fri, 4 Aug 2017 13:00:10 -0400 Subject: [PATCH 300/547] 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 13f34cb6f..d14d1e376 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 212aa2c0e1455edf506cd9dcae21cd6a6a5e6202 Mon Sep 17 00:00:00 2001 From: Aditya Mukerjee Date: Fri, 4 Aug 2017 13:00:43 -0400 Subject: [PATCH 301/547] 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 5bf582ef2..1fad45e08 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 b3903b3ef74e46428999f3999b9d56a54f5cb3fd Mon Sep 17 00:00:00 2001 From: Aditya Mukerjee Date: Fri, 4 Aug 2017 13:01:04 -0400 Subject: [PATCH 302/547] 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 d14d1e376..4fea7514e 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 1fe1e2e3092d60b27a261c2851f44e9abb1eae41 Mon Sep 17 00:00:00 2001 From: Ken Faulkner Date: Sun, 20 Aug 2017 12:47:38 +1000 Subject: [PATCH 303/547] Added reference to AzureTableHook --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 83b3b8066..5f656c3e1 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/) | From fa494e5a120b037129060517c404d6a7042fabb4 Mon Sep 17 00:00:00 2001 From: Albert Nigmatzianov Date: Sat, 2 Sep 2017 15:03:37 +0500 Subject: [PATCH 304/547] Fix typo in docs for New() --- logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logger.go b/logger.go index fdaf8a653..ebd4caf52 100644 --- a/logger.go +++ b/logger.go @@ -58,7 +58,7 @@ func (mw *MutexWrap) Disable() { // `Out` and `Hooks` directly on the default logger instance. You can also just // instantiate your own: // -// var log = &Logger{ +// var log = &logrus.Logger{ // Out: os.Stderr, // Formatter: new(JSONFormatter), // Hooks: make(LevelHooks), From 084c3c522f6b2c984a40c819a6e682ae96e14f46 Mon Sep 17 00:00:00 2001 From: Albert Nigmatzianov Date: Sat, 2 Sep 2017 15:04:38 +0500 Subject: [PATCH 305/547] Fix typo in docs for New() --- logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logger.go b/logger.go index ebd4caf52..879b13aea 100644 --- a/logger.go +++ b/logger.go @@ -60,7 +60,7 @@ func (mw *MutexWrap) Disable() { // // var log = &logrus.Logger{ // Out: os.Stderr, -// Formatter: new(JSONFormatter), +// Formatter: new(logrus.JSONFormatter), // Hooks: make(LevelHooks), // Level: logrus.DebugLevel, // } From ea3ae640ef820465e602abd8913cbb4c973301f7 Mon Sep 17 00:00:00 2001 From: Albert Nigmatzianov Date: Sat, 2 Sep 2017 15:05:25 +0500 Subject: [PATCH 306/547] Fix typo in docs for New() --- logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logger.go b/logger.go index 879b13aea..71f8bebfd 100644 --- a/logger.go +++ b/logger.go @@ -61,7 +61,7 @@ func (mw *MutexWrap) Disable() { // var log = &logrus.Logger{ // Out: os.Stderr, // Formatter: new(logrus.JSONFormatter), -// Hooks: make(LevelHooks), +// Hooks: make(logrus.LevelHooks), // Level: logrus.DebugLevel, // } // From 85c3f9f20ea7999c42b442c8daa86d85edaa316a Mon Sep 17 00:00:00 2001 From: Ernesto Alejo Date: Wed, 6 Sep 2017 19:34:58 +0200 Subject: [PATCH 307/547] Split terminal check to add build tags to support App Engine. --- terminal_check_appengine.go | 11 +++++++++++ terminal_check_notappengine.go | 19 +++++++++++++++++++ text_formatter.go | 15 +-------------- 3 files changed, 31 insertions(+), 14 deletions(-) create mode 100644 terminal_check_appengine.go create mode 100644 terminal_check_notappengine.go diff --git a/terminal_check_appengine.go b/terminal_check_appengine.go new file mode 100644 index 000000000..2403de981 --- /dev/null +++ b/terminal_check_appengine.go @@ -0,0 +1,11 @@ +// +build appengine + +package logrus + +import ( + "io" +) + +func checkIfTerminal(w io.Writer) bool { + return true +} diff --git a/terminal_check_notappengine.go b/terminal_check_notappengine.go new file mode 100644 index 000000000..116bcb4e3 --- /dev/null +++ b/terminal_check_notappengine.go @@ -0,0 +1,19 @@ +// +build !appengine + +package logrus + +import ( + "io" + "os" + + "golang.org/x/crypto/ssh/terminal" +) + +func checkIfTerminal(w io.Writer) bool { + switch v := w.(type) { + case *os.File: + return terminal.IsTerminal(int(v.Fd())) + default: + return false + } +} diff --git a/text_formatter.go b/text_formatter.go index be412aa94..61b21caea 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -3,14 +3,10 @@ package logrus import ( "bytes" "fmt" - "io" - "os" "sort" "strings" "sync" "time" - - "golang.org/x/crypto/ssh/terminal" ) const ( @@ -65,16 +61,7 @@ type TextFormatter struct { func (f *TextFormatter) init(entry *Entry) { if entry.Logger != nil { - 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 + f.isTerminal = checkIfTerminal(entry.Logger.Out) } } From dbb6b898faa0a16cbc57267a68666d8f97152b99 Mon Sep 17 00:00:00 2001 From: Marc CARRE Date: Tue, 3 Oct 2017 15:08:46 +0100 Subject: [PATCH 308/547] Add promrus to list of hooks. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5f656c3e1..5394166c8 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [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) | +| [Promrus](https://github.com/weaveworks/promrus) | Expose number of log messages as [Prometheus](https://prometheus.io/) metrics | | [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) | From 9db93150fc17c45ea94c0266a0306aa40d4dc926 Mon Sep 17 00:00:00 2001 From: Aditya Mukerjee Date: Mon, 9 Oct 2017 11:18:43 -0400 Subject: [PATCH 309/547] Fix typo in docstring --- logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logger.go b/logger.go index fdaf8a653..03a5de239 100644 --- a/logger.go +++ b/logger.go @@ -10,7 +10,7 @@ import ( type Logger struct { // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a // file, or leave it default which is `os.Stderr`. You can also set this to - // something more adventorous, such as logging to Kafka. + // something more adventurous, such as logging to Kafka. Out io.Writer // Hooks for the logger instance. These allow firing events based on logging // levels and log entries. For example, to send errors to an error tracking From 3fd8ee336041128bbb505bd17d38d034cca752be Mon Sep 17 00:00:00 2001 From: Eric Marden Date: Thu, 26 Oct 2017 13:58:09 -0500 Subject: [PATCH 310/547] Adds `logbeat` hook to README Adds link to new hook for sending logrus entries to Opbeat service. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5f656c3e1..dd9c39633 100644 --- a/README.md +++ b/README.md @@ -263,6 +263,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/tracer0tong/kafkalogrus) | Hook for logging to Kafka | | [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem | +| [Logbeat](https://github.com/macandmia/logbeat) | Hook for logging to [Opbeat](https://opbeat.com/) | | [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/) | From 37237f1fed20ea6e066221e2d8ef61e1258674b6 Mon Sep 17 00:00:00 2001 From: Felix Glaser Date: Wed, 1 Nov 2017 14:11:54 -0400 Subject: [PATCH 311/547] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f656c3e1..c822248d8 100644 --- a/README.md +++ b/README.md @@ -374,7 +374,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. +* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be 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 dc0b32f8e88939c7b166bf999bb293df332ec47f Mon Sep 17 00:00:00 2001 From: Marianne Feng Date: Mon, 6 Nov 2017 16:19:47 -0800 Subject: [PATCH 312/547] added pretty print option for json logs --- README.md | 2 ++ json_formatter.go | 13 ++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f656c3e1..f13335efb 100644 --- a/README.md +++ b/README.md @@ -1,5 +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) +**this is an exact clone of the Logrus repo [here](https://github.com/sirupsen/logrus), the only change that has been made is json formatter takes in a setting to pretty print json logs for easier debugging** + Logrus is a structured logger for Go (golang), completely API compatible with the standard library logger. diff --git a/json_formatter.go b/json_formatter.go index fb01c1b10..e52ab177d 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -43,6 +43,9 @@ type JSONFormatter struct { // }, // } FieldMap FieldMap + + // PrettyPrint will indent all json logs + PrettyPrint bool } // Format renders a single log entry @@ -71,7 +74,15 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() - serialized, err := json.Marshal(data) + var serialized []byte + var err error + + if f.PrettyPrint { + serialized, err = json.MarshalIndent(data, "", "\t") + } else { + serialized, err = json.Marshal(data) + } + if err != nil { return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) } From bcab42ac2a65a2484d78b5707c6cfa307ed8d18a Mon Sep 17 00:00:00 2001 From: Marianne Feng Date: Mon, 6 Nov 2017 16:28:37 -0800 Subject: [PATCH 313/547] removed useless line from readme --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index f13335efb..5f656c3e1 100644 --- a/README.md +++ b/README.md @@ -1,7 +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) -**this is an exact clone of the Logrus repo [here](https://github.com/sirupsen/logrus), the only change that has been made is json formatter takes in a setting to pretty print json logs for easier debugging** - Logrus is a structured logger for Go (golang), completely API compatible with the standard library logger. From 6d6d8d4884b7fc3ecd7c887c81fc11ef2b9adcb1 Mon Sep 17 00:00:00 2001 From: Yen-Cheng Chou Date: Tue, 14 Nov 2017 16:58:00 -0500 Subject: [PATCH 314/547] Fix typo in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f656c3e1..c822248d8 100644 --- a/README.md +++ b/README.md @@ -374,7 +374,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. +* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be 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 ed85cb7c14e45e6c88e54dbac0543dffe390c72a Mon Sep 17 00:00:00 2001 From: Neil Isaac Date: Tue, 21 Nov 2017 22:43:47 -0500 Subject: [PATCH 315/547] Add FieldMap support to TestFormatter --- text_formatter.go | 15 ++++++++++++--- text_formatter_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index be412aa94..85928256e 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -60,6 +60,15 @@ type TextFormatter struct { // Whether the logger's out is to a terminal isTerminal bool + // FieldMap allows users to customize the names of keys for default fields. + // As an example: + // formatter := &JSONFormatter{ + // FieldMap: FieldMap{ + // FieldKeyTime: "@timestamp", + // FieldKeyLevel: "@level", + // FieldKeyMsg: "@message"}} + FieldMap FieldMap + sync.Once } @@ -109,11 +118,11 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { f.printColored(b, entry, keys, timestampFormat) } else { if !f.DisableTimestamp { - f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat)) + f.appendKeyValue(b, f.FieldMap.resolve(FieldKeyTime), entry.Time.Format(timestampFormat)) } - f.appendKeyValue(b, "level", entry.Level.String()) + f.appendKeyValue(b, f.FieldMap.resolve(FieldKeyLevel), entry.Level.String()) if entry.Message != "" { - f.appendKeyValue(b, "msg", entry.Message) + f.appendKeyValue(b, f.FieldMap.resolve(FieldKeyMsg), entry.Message) } for _, key := range keys { f.appendKeyValue(b, key, entry.Data[key]) diff --git a/text_formatter_test.go b/text_formatter_test.go index d93b931e5..789d52df2 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -7,6 +7,8 @@ import ( "strings" "testing" "time" + + "github.com/stretchr/testify/assert" ) func TestFormatting(t *testing.T) { @@ -137,5 +139,32 @@ func TestDisableTimestampWithColoredOutput(t *testing.T) { } } +func TestTextFormatterFieldMap(t *testing.T) { + formatter := &TextFormatter{ + DisableColors: true, + FieldMap: FieldMap{ + FieldKeyMsg: "message", + FieldKeyLevel: "somelevel", + FieldKeyTime: "timeywimey", + }, + } + + entry := &Entry{ + Message: "oh hi", + Level: WarnLevel, + Time: time.Date(1981, time.February, 24, 4, 28, 3, 100, time.UTC), + } + + b, err := formatter.Format(entry) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + + assert.Equal(t, + `timeywimey="1981-02-24T04:28:03Z" somelevel=warning message="oh hi"`+"\n", + string(b), + "Formatted doesn't respect correct FieldMap") +} + // TODO add tests for sorting etc., this requires a parser for the text // formatter output. From c2337e61f690c6cab99e548555eda1e89942746f Mon Sep 17 00:00:00 2001 From: Neil Isaac Date: Tue, 21 Nov 2017 22:56:37 -0500 Subject: [PATCH 316/547] fix example --- text_formatter.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index 85928256e..aa0694cdf 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -62,11 +62,11 @@ type TextFormatter struct { // FieldMap allows users to customize the names of keys for default fields. // As an example: - // formatter := &JSONFormatter{ + // formatter := &TextFormatter{ // FieldMap: FieldMap{ - // FieldKeyTime: "@timestamp", + // FieldKeyTime: "@timestamp", // FieldKeyLevel: "@level", - // FieldKeyMsg: "@message"}} + // FieldKeyMsg: "@message"}} FieldMap FieldMap sync.Once From 7200332d6c95341f1be60d27189e408589f6871c Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 5 Dec 2017 15:32:29 -0500 Subject: [PATCH 317/547] changelog: 1.0.4 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8236d8b6e..cc58f6451 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.0.4 + +* Fix race when adding hooks (#612) +* Fix terminal check in AppEngine (#635) + # 1.0.3 * Replace example files with testable examples From 8ddf57c2d94c45bc7dfeb2fe6c0bc0287503e4eb Mon Sep 17 00:00:00 2001 From: conor Date: Thu, 21 Dec 2017 14:04:49 -0500 Subject: [PATCH 318/547] mirror and wrap Logger instance methods in exported.go --- .gitignore | 1 + exported.go | 16 +++------------- logger.go | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 66be63a00..c722487ae 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ logrus +.idea \ No newline at end of file diff --git a/exported.go b/exported.go index 013183eda..141e95100 100644 --- a/exported.go +++ b/exported.go @@ -15,36 +15,26 @@ func StandardLogger() *Logger { // SetOutput sets the standard logger output. func SetOutput(out io.Writer) { - std.mu.Lock() - defer std.mu.Unlock() - std.Out = out + std.SetOutput(out) } // SetFormatter sets the standard logger formatter. func SetFormatter(formatter Formatter) { - std.mu.Lock() - defer std.mu.Unlock() - std.Formatter = formatter + std.SetFormatter(formatter) } // SetLevel sets the standard logger level. func SetLevel(level Level) { - std.mu.Lock() - defer std.mu.Unlock() std.SetLevel(level) } // GetLevel returns the standard logger level. func GetLevel() Level { - std.mu.Lock() - defer std.mu.Unlock() - return std.level() + return std.GetLevel() } // AddHook adds a hook to the standard logger hooks. func AddHook(hook Hook) { - std.mu.Lock() - defer std.mu.Unlock() std.Hooks.Add(hook) } diff --git a/logger.go b/logger.go index fdaf8a653..91617fccd 100644 --- a/logger.go +++ b/logger.go @@ -312,12 +312,33 @@ func (logger *Logger) level() Level { return Level(atomic.LoadUint32((*uint32)(&logger.Level))) } +// SetLevel sets the logger level. func (logger *Logger) SetLevel(level Level) { atomic.StoreUint32((*uint32)(&logger.Level), uint32(level)) } +// GetLevel returns the logger level. +func (logger *Logger) GetLevel() Level { + return logger.level() +} + +// AddHook adds a hook to the logger hooks. func (logger *Logger) AddHook(hook Hook) { logger.mu.Lock() defer logger.mu.Unlock() logger.Hooks.Add(hook) } + +// SetFormatter sets the logger formatter. +func (logger *Logger) SetFormatter(formatter Formatter) { + logger.mu.Lock() + defer logger.mu.Unlock() + logger.Formatter = formatter +} + +// SetOutput sets the logger output. +func (logger *Logger) SetOutput(output io.Writer) { + logger.mu.Lock() + defer logger.mu.Unlock() + logger.Out = output +} From 6e6d57b465739d53431734709fff424f5fc0b24b Mon Sep 17 00:00:00 2001 From: conor Date: Thu, 21 Dec 2017 14:10:48 -0500 Subject: [PATCH 319/547] remove .gitignore changes --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index c722487ae..bb7e11136 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -logrus -.idea \ No newline at end of file +logrus \ No newline at end of file From 84b8481d847d9000359c8f9af4066e6257ce1705 Mon Sep 17 00:00:00 2001 From: conor Date: Thu, 21 Dec 2017 14:16:49 -0500 Subject: [PATCH 320/547] remove .gitignore changes and update AddHook --- .gitignore | 2 +- exported.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index bb7e11136..66be63a00 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -logrus \ No newline at end of file +logrus diff --git a/exported.go b/exported.go index 141e95100..e5e484f9b 100644 --- a/exported.go +++ b/exported.go @@ -35,7 +35,7 @@ func GetLevel() Level { // AddHook adds a hook to the standard logger hooks. func AddHook(hook Hook) { - std.Hooks.Add(hook) + std.AddHook(hook) } // WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key. From 47ea2486de7b6ed172f662f2e62dedf87b3c7280 Mon Sep 17 00:00:00 2001 From: Dennis de Reus Date: Fri, 29 Dec 2017 20:26:35 +0100 Subject: [PATCH 321/547] TextFormatter behaviour aligned with stdlib log (fixes #167) stdlib `log` adds a newline at the end of a message if none is present, otherwise does not. Before this change logrus would always add a newline, resulting in inconsistent behaviour if stdlib log was replaced with logrus, and a user would e.g. use 'log.printf("test\n")' --- text_formatter.go | 4 ++++ text_formatter_test.go | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/text_formatter.go b/text_formatter.go index 61b21caea..9f7dc35b8 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -126,6 +126,10 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin levelText := strings.ToUpper(entry.Level.String())[0:4] + // Remove a single newline if it already exists in the message to keep + // the behavior of logrus text_formatter the same as the stdlib log package + entry.Message = strings.TrimSuffix(entry.Message, "\n") + if f.DisableTimestamp { fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s ", levelColor, levelText, entry.Message) } else if !f.FullTimestamp { diff --git a/text_formatter_test.go b/text_formatter_test.go index d93b931e5..aea8727ac 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -137,5 +137,28 @@ func TestDisableTimestampWithColoredOutput(t *testing.T) { } } +func TestNewlineBehavior(t *testing.T) { + tf := &TextFormatter{ForceColors: true} + + // Ensure a single new line is removed as per stdlib log + e := NewEntry(StandardLogger()) + e.Message = "test message\n" + b, _ := tf.Format(e) + if bytes.Contains(b, []byte("test message\n")) { + t.Error("first newline at end of Entry.Message resulted in unexpected 2 newlines in output. Expected newline to be removed.") + } + + // Ensure a double new line is reduced to a single new line + e = NewEntry(StandardLogger()) + e.Message = "test message\n\n" + b, _ = tf.Format(e) + if bytes.Contains(b, []byte("test message\n\n")) { + t.Error("Double newline at end of Entry.Message resulted in unexpected 2 newlines in output. Expected single newline") + } + if !bytes.Contains(b, []byte("test message\n")) { + t.Error("Double newline at end of Entry.Message did not result in a single newline after formatting") + } +} + // TODO add tests for sorting etc., this requires a parser for the text // formatter output. From 2748f717dccab61775f17b213860a48fe9617671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Linhares?= Date: Mon, 22 Jan 2018 10:35:26 -0500 Subject: [PATCH 322/547] Fix deadlock on panics at Entry.log When calling Entry.log a panic inside some of the locking blocks could cause the whole logger to deadlock. One of the ways this could happen is for a hook to cause a panic, when this happens the lock is never unlocked and the library deadlocks, causing the code that is calling it to deadlock as well. This changes how locking happens with unlocks at defer blocks so even if a panic happens somewhere along the log call the library will still unlock and continue to function. --- entry.go | 47 +++++++++++++++++++++++++++-------------------- entry_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 20 deletions(-) diff --git a/entry.go b/entry.go index 1fad45e08..df6f92dc8 100644 --- a/entry.go +++ b/entry.go @@ -94,38 +94,45 @@ func (entry Entry) log(level Level, msg string) { entry.Level = level entry.Message = msg - 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() - } + entry.fireHooks() + buffer = bufferPool.Get().(*bytes.Buffer) buffer.Reset() defer bufferPool.Put(buffer) entry.Buffer = buffer - serialized, err := entry.Logger.Formatter.Format(&entry) + + entry.write() + entry.Buffer = nil + + // To avoid Entry#log() returning a value that only would make sense for + // panic() to use in Entry#Panic(), we avoid the allocation by checking + // directly here. + if level <= PanicLevel { + panic(&entry) + } +} + +func (entry *Entry) fireHooks() { + entry.Logger.mu.Lock() + defer entry.Logger.mu.Unlock() + err := entry.Logger.Hooks.Fire(entry.Level, entry) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) + } +} + +func (entry *Entry) write() { + serialized, err := entry.Logger.Formatter.Format(entry) + entry.Logger.mu.Lock() + defer entry.Logger.mu.Unlock() if err != nil { - entry.Logger.mu.Lock() fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) - entry.Logger.mu.Unlock() } else { - entry.Logger.mu.Lock() _, err = entry.Logger.Out.Write(serialized) if err != nil { fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) } - entry.Logger.mu.Unlock() - } - - // To avoid Entry#log() returning a value that only would make sense for - // panic() to use in Entry#Panic(), we avoid the allocation by checking - // directly here. - if level <= PanicLevel { - panic(&entry) } } diff --git a/entry_test.go b/entry_test.go index 99c3b41d5..a81e2b383 100644 --- a/entry_test.go +++ b/entry_test.go @@ -75,3 +75,41 @@ func TestEntryPanicf(t *testing.T) { entry := NewEntry(logger) entry.WithField("err", errBoom).Panicf("kaboom %v", true) } + +const ( + badMessage = "this is going to panic" + panicMessage = "this is broken" +) + +type panickyHook struct{} + +func (p *panickyHook) Levels() []Level { + return []Level{InfoLevel} +} + +func (p *panickyHook) Fire(entry *Entry) error { + if entry.Message == badMessage { + panic(panicMessage) + } + + return nil +} + +func TestEntryHooksPanic(t *testing.T) { + logger := New() + logger.Out = &bytes.Buffer{} + logger.Level = InfoLevel + logger.Hooks.Add(&panickyHook{}) + + defer func() { + p := recover() + assert.NotNil(t, p) + assert.Equal(t, panicMessage, p) + + entry := NewEntry(logger) + entry.Info("another message") + }() + + entry := NewEntry(logger) + entry.Info(badMessage) +} From 4c8329b6e400126e5c0353d9ad8594698a7e6a95 Mon Sep 17 00:00:00 2001 From: Joni Collinge Date: Tue, 30 Jan 2018 15:58:49 +0000 Subject: [PATCH 323/547] Add Application Insights hook to README Adds README link to Application Insights hook --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 08584b5fc..213e651a7 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) | +| [Application Insights](https://github.com/jjcollinge/logrus-appinsights) | Hook for [Application Insights](https://azure.microsoft.com/en-us/services/application-insights/) | [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 | From 1abc0d9f869f7c82a162c2e074a205fc5932f838 Mon Sep 17 00:00:00 2001 From: Joni Collinge Date: Tue, 30 Jan 2018 16:00:33 +0000 Subject: [PATCH 324/547] Made text consistent with other hooks --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 213e651a7..f986a5e18 100644 --- a/README.md +++ b/README.md @@ -247,7 +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) | -| [Application Insights](https://github.com/jjcollinge/logrus-appinsights) | Hook for [Application Insights](https://azure.microsoft.com/en-us/services/application-insights/) +| [Application Insights](https://github.com/jjcollinge/logrus-appinsights) | Hook for logging to [Application Insights](https://azure.microsoft.com/en-us/services/application-insights/) | [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 | From ea8f5c7fb749db5566deeca41e05822869d93d89 Mon Sep 17 00:00:00 2001 From: Michael Haines Date: Mon, 5 Feb 2018 12:42:00 -0700 Subject: [PATCH 325/547] Add failing test for using a FieldLogger with hooks inside goroutines --- logrus_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/logrus_test.go b/logrus_test.go index 78cbc2825..1585709df 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -343,6 +343,24 @@ func TestLoggingRace(t *testing.T) { wg.Wait() } +func TestLoggingRaceWithHooksOnFieldLogger(t *testing.T) { + logger := New() + hook := new(ModifyHook) + logger.AddHook(hook) + fieldLogger := logger.WithField("context", "clue") + + var wg sync.WaitGroup + wg.Add(100) + + for i := 0; i < 100; i++ { + go func() { + fieldLogger.Info("info") + wg.Done() + }() + } + wg.Wait() +} + // Compile test func TestLogrusInterface(t *testing.T) { var buffer bytes.Buffer From d6999d257fcaccfc94f6828beefc1c17ee71059e Mon Sep 17 00:00:00 2001 From: Michael Haines Date: Mon, 5 Feb 2018 12:44:11 -0700 Subject: [PATCH 326/547] Lock mutex before formatting to avoid race --- entry.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entry.go b/entry.go index df6f92dc8..24ded45fd 100644 --- a/entry.go +++ b/entry.go @@ -123,9 +123,9 @@ func (entry *Entry) fireHooks() { } func (entry *Entry) write() { - serialized, err := entry.Logger.Formatter.Format(entry) entry.Logger.mu.Lock() defer entry.Logger.mu.Unlock() + serialized, err := entry.Logger.Formatter.Format(entry) if err != nil { fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) } else { From 1cd8e53785e43736447f3eef42e89035e245f222 Mon Sep 17 00:00:00 2001 From: Michael Haines Date: Mon, 5 Feb 2018 12:52:11 -0700 Subject: [PATCH 327/547] rename fieldLogger to entry --- logrus_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/logrus_test.go b/logrus_test.go index 1585709df..cd176029e 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -343,18 +343,18 @@ func TestLoggingRace(t *testing.T) { wg.Wait() } -func TestLoggingRaceWithHooksOnFieldLogger(t *testing.T) { +func TestLoggingRaceWithHooksOnEntry(t *testing.T) { logger := New() hook := new(ModifyHook) logger.AddHook(hook) - fieldLogger := logger.WithField("context", "clue") + entry := logger.WithField("context", "clue") var wg sync.WaitGroup wg.Add(100) for i := 0; i < 100; i++ { go func() { - fieldLogger.Info("info") + entry.Info("info") wg.Done() }() } From 85aeccaeee79fddd5437748d03f028e486e7a686 Mon Sep 17 00:00:00 2001 From: Phillip Johnsen Date: Mon, 5 Feb 2018 21:59:23 +0100 Subject: [PATCH 328/547] Fix typo in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 08584b5fc..e5da2c2b1 100644 --- a/README.md +++ b/README.md @@ -220,7 +220,7 @@ Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in ```go import ( log "github.com/sirupsen/logrus" - "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake" + "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake" logrus_syslog "github.com/sirupsen/logrus/hooks/syslog" "log/syslog" ) From 92b81dccd27a4b5b8173f60840d72931d3cf4ca9 Mon Sep 17 00:00:00 2001 From: Jay Ching Lim Date: Mon, 12 Feb 2018 17:26:48 -0500 Subject: [PATCH 329/547] Make fireHooks() method receive a copy of Entry structure to avoid race conditions Signed-off-by: Jay Ching Lim --- entry.go | 6 ++++-- hooks/test/test_test.go | 24 +++++++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/entry.go b/entry.go index df6f92dc8..778f4c9f0 100644 --- a/entry.go +++ b/entry.go @@ -113,10 +113,12 @@ func (entry Entry) log(level Level, msg string) { } } -func (entry *Entry) fireHooks() { +// This function is not declared with a pointer value because otherwise +// race conditions will occur when using multiple goroutines +func (entry Entry) fireHooks() { entry.Logger.mu.Lock() defer entry.Logger.mu.Unlock() - err := entry.Logger.Hooks.Fire(entry.Level, entry) + err := entry.Logger.Hooks.Fire(entry.Level, &entry) if err != nil { fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) } diff --git a/hooks/test/test_test.go b/hooks/test/test_test.go index 3f55cfe31..dea768e6c 100644 --- a/hooks/test/test_test.go +++ b/hooks/test/test_test.go @@ -1,6 +1,7 @@ package test import ( + "sync" "testing" "github.com/sirupsen/logrus" @@ -8,7 +9,6 @@ import ( ) func TestAllHooks(t *testing.T) { - assert := assert.New(t) logger, hook := NewNullLogger() @@ -35,5 +35,27 @@ func TestAllHooks(t *testing.T) { assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level) assert.Equal("Hello error", hook.LastEntry().Message) assert.Equal(1, len(hook.Entries)) +} + +func TestLoggingWithHooksRace(t *testing.T) { + assert := assert.New(t) + logger, hook := NewNullLogger() + + var wg sync.WaitGroup + wg.Add(100) + + for i := 0; i < 100; i++ { + go func() { + logger.Info("info") + wg.Done() + }() + } + + assert.Equal(logrus.InfoLevel, hook.LastEntry().Level) + assert.Equal("info", hook.LastEntry().Message) + + wg.Wait() + entries := hook.AllEntries() + assert.Equal(100, len(entries)) } From 8f4f1fca1675b3d7919e9ef4ec2b1a3f196b0808 Mon Sep 17 00:00:00 2001 From: Lyra Naeseth Date: Tue, 13 Feb 2018 17:44:51 -0800 Subject: [PATCH 330/547] Have prefixFieldClashes respect the JSON FieldMap Currently, it will fix clashes that don't exist and miss clashes that do exist due to field remapping. Fixes #708 --- formatter.go | 17 +++++++------ json_formatter.go | 2 +- json_formatter_test.go | 55 ++++++++++++++++++++++++++++++++++++++++++ text_formatter.go | 3 ++- 4 files changed, 68 insertions(+), 9 deletions(-) diff --git a/formatter.go b/formatter.go index b183ff5b1..849dc8b97 100644 --- a/formatter.go +++ b/formatter.go @@ -30,16 +30,19 @@ type Formatter interface { // // It's not exported because it's still using Data in an opinionated way. It's to // avoid code duplication between the two default formatters. -func prefixFieldClashes(data Fields) { - if t, ok := data["time"]; ok { - data["fields.time"] = t +func prefixFieldClashes(data Fields, fieldMap FieldMap) { + timeKey := fieldMap.resolve(FieldKeyTime) + if t, ok := data[timeKey]; ok { + data["fields."+timeKey] = t } - if m, ok := data["msg"]; ok { - data["fields.msg"] = m + msgKey := fieldMap.resolve(FieldKeyMsg) + if m, ok := data[msgKey]; ok { + data["fields."+msgKey] = m } - if l, ok := data["level"]; ok { - data["fields.level"] = l + levelKey := fieldMap.resolve(FieldKeyLevel) + if l, ok := data[levelKey]; ok { + data["fields."+levelKey] = l } } diff --git a/json_formatter.go b/json_formatter.go index fb01c1b10..706494732 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -58,7 +58,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { data[k] = v } } - prefixFieldClashes(data) + prefixFieldClashes(data, f.FieldMap) timestampFormat := f.TimestampFormat if timestampFormat == "" { diff --git a/json_formatter_test.go b/json_formatter_test.go index 51093a79b..1c140d045 100644 --- a/json_formatter_test.go +++ b/json_formatter_test.go @@ -3,6 +3,7 @@ package logrus import ( "encoding/json" "errors" + "fmt" "strings" "testing" ) @@ -106,6 +107,60 @@ func TestFieldClashWithLevel(t *testing.T) { } } +func TestFieldClashWithRemappedFields(t *testing.T) { + formatter := &JSONFormatter{ + FieldMap: FieldMap{ + FieldKeyTime: "@timestamp", + FieldKeyLevel: "@level", + FieldKeyMsg: "@message", + }, + } + + b, err := formatter.Format(WithFields(Fields{ + "@timestamp": "@timestamp", + "@level": "@level", + "@message": "@message", + "timestamp": "timestamp", + "level": "level", + "msg": "msg", + })) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + + entry := make(map[string]interface{}) + err = json.Unmarshal(b, &entry) + if err != nil { + t.Fatal("Unable to unmarshal formatted entry: ", err) + } + + for _, field := range []string{"timestamp", "level", "msg"} { + if entry[field] != field { + t.Errorf("Expected field %v to be untouched; got %v", field, entry[field]) + } + + remappedKey := fmt.Sprintf("fields.%s", field) + if remapped, ok := entry[remappedKey]; ok { + t.Errorf("Expected %s to be empty; got %v", remappedKey, remapped) + } + } + + for _, field := range []string{"@timestamp", "@level", "@message"} { + if entry[field] == field { + t.Errorf("Expected field %v to be mapped to an Entry value", field) + } + + remappedKey := fmt.Sprintf("fields.%s", field) + if remapped, ok := entry[remappedKey]; ok { + if remapped != field { + t.Errorf("Expected field %v to be copied to %s; got %v", field, remappedKey, remapped) + } + } else { + t.Errorf("Expected field %v to be copied to %s; was absent", field, remappedKey) + } + } +} + func TestJSONEntryEndsWithNewline(t *testing.T) { formatter := &JSONFormatter{} diff --git a/text_formatter.go b/text_formatter.go index 61b21caea..62763ebe9 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -20,6 +20,7 @@ const ( var ( baseTimestamp time.Time + emptyFieldMap FieldMap ) func init() { @@ -82,7 +83,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { b = &bytes.Buffer{} } - prefixFieldClashes(entry.Data) + prefixFieldClashes(entry.Data, emptyFieldMap) f.Do(func() { f.init(entry) }) From b1397e6f171902faaa95291c7d0a0787bb221fa3 Mon Sep 17 00:00:00 2001 From: Andrew Rezcov Date: Fri, 16 Feb 2018 16:50:54 +0300 Subject: [PATCH 331/547] reamde: add logrus-clickhouse-hook --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bc3f9bc09..be18d2f0a 100644 --- a/README.md +++ b/README.md @@ -250,6 +250,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [Application Insights](https://github.com/jjcollinge/logrus-appinsights) | Hook for logging to [Application Insights](https://azure.microsoft.com/en-us/services/application-insights/) | [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. | +| [ClickHouse](https://github.com/oxgrouby/logrus-clickhouse-hook) | Send logs to [ClickHouse](https://clickhouse.yandex/) | | [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| From f150b54695562e397a9c6368f730531ec213132d Mon Sep 17 00:00:00 2001 From: earlzo Date: Tue, 20 Feb 2018 16:28:12 +0800 Subject: [PATCH 332/547] Fixed: comment --- entry.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entry.go b/entry.go index 778f4c9f0..d075d7237 100644 --- a/entry.go +++ b/entry.go @@ -48,7 +48,7 @@ type Entry struct { func NewEntry(logger *Logger) *Entry { return &Entry{ Logger: logger, - // Default is three fields, give a little extra room + // Default is five fields, give a little extra room Data: make(Fields, 5), } } From a7f7434427a5c18a0df72f966e5de52e2e6c0eba Mon Sep 17 00:00:00 2001 From: Grace Noah Date: Fri, 23 Feb 2018 21:35:25 +0000 Subject: [PATCH 333/547] add gopherjs build tag it behaves the same way as the appengine tag fix #716 --- terminal_bsd.go | 2 +- terminal_check_appengine.go | 2 +- terminal_check_notappengine.go | 2 +- terminal_linux.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/terminal_bsd.go b/terminal_bsd.go index d7b3893f3..4880d13d2 100644 --- a/terminal_bsd.go +++ b/terminal_bsd.go @@ -1,5 +1,5 @@ // +build darwin freebsd openbsd netbsd dragonfly -// +build !appengine +// +build !appengine,!gopherjs package logrus diff --git a/terminal_check_appengine.go b/terminal_check_appengine.go index 2403de981..3de08e802 100644 --- a/terminal_check_appengine.go +++ b/terminal_check_appengine.go @@ -1,4 +1,4 @@ -// +build appengine +// +build appengine gopherjs package logrus diff --git a/terminal_check_notappengine.go b/terminal_check_notappengine.go index 116bcb4e3..067047a12 100644 --- a/terminal_check_notappengine.go +++ b/terminal_check_notappengine.go @@ -1,4 +1,4 @@ -// +build !appengine +// +build !appengine,!gopherjs package logrus diff --git a/terminal_linux.go b/terminal_linux.go index 88d7298e2..f29a0097c 100644 --- a/terminal_linux.go +++ b/terminal_linux.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 !appengine +// +build !appengine,!gopherjs package logrus From d1ef23ae97d8fed9e628d76eba30d5b3cd7e82e3 Mon Sep 17 00:00:00 2001 From: Dylan Meissner Date: Sun, 11 Feb 2018 07:37:38 -0800 Subject: [PATCH 334/547] Add Kafka REST Proxy hook to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bc3f9bc09..f77819b16 100644 --- a/README.md +++ b/README.md @@ -263,6 +263,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [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/tracer0tong/kafkalogrus) | Hook for logging to Kafka | +| [Kafka REST Proxy](https://github.com/Nordstrom/logrus-kafka-rest-proxy) | Hook for logging to [Kafka REST Proxy](https://docs.confluent.io/current/kafka-rest/docs) | | [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem | | [Logbeat](https://github.com/macandmia/logbeat) | Hook for logging to [Opbeat](https://opbeat.com/) | | [Logentries](https://github.com/jcftang/logentriesrus) | Hook for logging to [Logentries](https://logentries.com/) | From 32cc20e165a78eb27bd67f6c2598663a018b154e Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Sun, 11 Mar 2018 18:51:37 -0400 Subject: [PATCH 335/547] changelog: add 1.0.5 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc58f6451..1bd1deb29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.0.5 + +* Fix hooks race (#707) +* Fix panic deadlock (#695) + # 1.0.4 * Fix race when adding hooks (#612) From 5f3231495366da8415ea9579ad67717add6aa060 Mon Sep 17 00:00:00 2001 From: Marianne Feng Date: Tue, 20 Mar 2018 18:20:51 -0700 Subject: [PATCH 336/547] changed prettyprinting to use spaces as opposed to /t --- json_formatter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json_formatter.go b/json_formatter.go index e52ab177d..dab5a1065 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -78,7 +78,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { var err error if f.PrettyPrint { - serialized, err = json.MarshalIndent(data, "", "\t") + serialized, err = json.MarshalIndent(data, "", " ") } else { serialized, err = json.Marshal(data) } From 958111a3e28bcf8682d9c1c2ea7f4eb836c5b698 Mon Sep 17 00:00:00 2001 From: Wilson Date: Fri, 9 Mar 2018 21:57:56 -0800 Subject: [PATCH 337/547] Fix run-on sentence --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f77819b16..1d8eb0d64 100644 --- a/README.md +++ b/README.md @@ -495,7 +495,7 @@ logrus.RegisterExitHandler(handler) #### Thread safety -By default Logger is protected by mutex for concurrent writes, this mutex is invoked when calling hooks and writing logs. +By default, Logger is protected by a mutex for concurrent writes. The mutex is held when calling hooks and writing logs. If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking. Situation when locking is not needed includes: From 004c737169e6dfc9b2123abe8b36cc128cab8e63 Mon Sep 17 00:00:00 2001 From: Olzhas Ilyubayev Date: Wed, 28 Mar 2018 12:10:14 +0600 Subject: [PATCH 338/547] delete dead link https://github.com/ripcurld00d/logrus-logzio-hook isn't available now --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 9751da1bf..e2dbb2767 100644 --- a/README.md +++ b/README.md @@ -294,7 +294,6 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [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 | | [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 df3c65051fa21c0146588d68f24a2768e674ddfe Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Tue, 3 Apr 2018 00:25:30 +0200 Subject: [PATCH 339/547] Added support for text coloring on Windows 10 --- text_formatter.go | 2 ++ text_formatter_linux.go | 6 ++++++ text_formatter_windows.go | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 text_formatter_linux.go create mode 100644 text_formatter_windows.go diff --git a/text_formatter.go b/text_formatter.go index ae91eddf0..afd9ffb66 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -67,6 +67,8 @@ type TextFormatter struct { func (f *TextFormatter) init(entry *Entry) { if entry.Logger != nil { f.isTerminal = checkIfTerminal(entry.Logger.Out) + + f.initTerminal(entry) } } diff --git a/text_formatter_linux.go b/text_formatter_linux.go new file mode 100644 index 000000000..e5fa6a966 --- /dev/null +++ b/text_formatter_linux.go @@ -0,0 +1,6 @@ +// +build !appengine,!gopherjs + +package logrus + +func (f *TextFormatter) initTerminal(entry *Entry) { +} diff --git a/text_formatter_windows.go b/text_formatter_windows.go new file mode 100644 index 000000000..552c5f35b --- /dev/null +++ b/text_formatter_windows.go @@ -0,0 +1,19 @@ +// +build !appengine,!gopherjs + +package logrus + +import ( + "os" + "syscall" + + sequences "github.com/konsorten/go-windows-terminal-sequences" +) + +func (f *TextFormatter) initTerminal(entry *Entry) { + switch v := entry.Logger.Out.(type) { + case *os.File: + handle := syscall.Handle(v.Fd()) + + sequences.EnableVirtualTerminalProcessing(handle, true) + } +} From d46d434b61506c5db4deed4b5491bd46e30d7817 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Tue, 3 Apr 2018 01:15:45 +0200 Subject: [PATCH 340/547] Improved building of non-windows code --- text_formatter_linux.go => text_formatter_nonwindows.go | 2 +- text_formatter_windows.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename text_formatter_linux.go => text_formatter_nonwindows.go (64%) diff --git a/text_formatter_linux.go b/text_formatter_nonwindows.go similarity index 64% rename from text_formatter_linux.go rename to text_formatter_nonwindows.go index e5fa6a966..f732b8ba7 100644 --- a/text_formatter_linux.go +++ b/text_formatter_nonwindows.go @@ -1,4 +1,4 @@ -// +build !appengine,!gopherjs +// +build !appengine,!gopherjs,!windows package logrus diff --git a/text_formatter_windows.go b/text_formatter_windows.go index 552c5f35b..c9045a017 100644 --- a/text_formatter_windows.go +++ b/text_formatter_windows.go @@ -1,4 +1,4 @@ -// +build !appengine,!gopherjs +// +build !appengine,!gopherjs,windows package logrus From 47363df10b77de883cb4f6f4b6505f23943a1ef7 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Tue, 3 Apr 2018 01:23:44 +0200 Subject: [PATCH 341/547] Extended conditions to include non-native builds --- text_formatter_nonwindows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text_formatter_nonwindows.go b/text_formatter_nonwindows.go index f732b8ba7..0c0802a29 100644 --- a/text_formatter_nonwindows.go +++ b/text_formatter_nonwindows.go @@ -1,4 +1,4 @@ -// +build !appengine,!gopherjs,!windows +// +build appengine gopherjs !windows package logrus From 5df684f54c1dfaa88536b0d122a1fc282ca0d61e Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Tue, 3 Apr 2018 04:40:58 +0200 Subject: [PATCH 342/547] Added terminal check on Windows --- terminal_check_notappengine.go | 2 +- terminal_check_windows.go | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 terminal_check_windows.go diff --git a/terminal_check_notappengine.go b/terminal_check_notappengine.go index 067047a12..e5a5186e5 100644 --- a/terminal_check_notappengine.go +++ b/terminal_check_notappengine.go @@ -1,4 +1,4 @@ -// +build !appengine,!gopherjs +// +build !appengine,!gopherjs,!windows package logrus diff --git a/terminal_check_windows.go b/terminal_check_windows.go new file mode 100644 index 000000000..17ebe8051 --- /dev/null +++ b/terminal_check_windows.go @@ -0,0 +1,20 @@ +// +build !appengine,!gopherjs,windows + +package logrus + +import ( + "io" + "os" + "syscall" +) + +func checkIfTerminal(w io.Writer) bool { + switch v := w.(type) { + case *os.File: + var mode uint32 + err := syscall.GetConsoleMode(syscall.Handle(v.Fd()), &mode) + return err == nil + default: + return false + } +} From a67267bfbcc8b3c1dc45c37b5a98fd209b26de06 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Tue, 3 Apr 2018 04:47:29 +0200 Subject: [PATCH 343/547] Simplified file structure --- terminal_bsd.go | 3 +++ terminal_linux.go | 3 +++ text_formatter_windows.go => terminal_windows.go | 0 text_formatter_nonwindows.go | 6 ------ 4 files changed, 6 insertions(+), 6 deletions(-) rename text_formatter_windows.go => terminal_windows.go (100%) delete mode 100644 text_formatter_nonwindows.go diff --git a/terminal_bsd.go b/terminal_bsd.go index 4880d13d2..3b20604f7 100644 --- a/terminal_bsd.go +++ b/terminal_bsd.go @@ -8,3 +8,6 @@ import "golang.org/x/sys/unix" const ioctlReadTermios = unix.TIOCGETA type Termios unix.Termios + +func (f *TextFormatter) initTerminal(entry *Entry) { +} diff --git a/terminal_linux.go b/terminal_linux.go index f29a0097c..78c4298dc 100644 --- a/terminal_linux.go +++ b/terminal_linux.go @@ -12,3 +12,6 @@ import "golang.org/x/sys/unix" const ioctlReadTermios = unix.TCGETS type Termios unix.Termios + +func (f *TextFormatter) initTerminal(entry *Entry) { +} diff --git a/text_formatter_windows.go b/terminal_windows.go similarity index 100% rename from text_formatter_windows.go rename to terminal_windows.go diff --git a/text_formatter_nonwindows.go b/text_formatter_nonwindows.go deleted file mode 100644 index 0c0802a29..000000000 --- a/text_formatter_nonwindows.go +++ /dev/null @@ -1,6 +0,0 @@ -// +build appengine gopherjs !windows - -package logrus - -func (f *TextFormatter) initTerminal(entry *Entry) { -} From d99b30724eaf2c02e0bdf7f699d444df23c0f534 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Tue, 3 Apr 2018 04:50:50 +0200 Subject: [PATCH 344/547] Fixed initTerminal() was run for non-terminals --- text_formatter.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index afd9ffb66..c62c412fc 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -51,7 +51,6 @@ type TextFormatter struct { // be desired. DisableSorting bool - // Disables the truncation of the level text to 4 characters. DisableLevelTruncation bool @@ -68,7 +67,9 @@ func (f *TextFormatter) init(entry *Entry) { if entry.Logger != nil { f.isTerminal = checkIfTerminal(entry.Logger.Out) - f.initTerminal(entry) + if f.isTerminal { + f.initTerminal(entry) + } } } From 0e00e6f239f6b4f334453147ce8b0a34bebfaa8f Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Tue, 3 Apr 2018 04:55:52 +0200 Subject: [PATCH 345/547] Unified terminal initialization code handling --- terminal_bsd.go | 8 ++++++-- terminal_linux.go | 8 ++++++-- terminal_windows.go | 9 ++++----- text_formatter.go | 2 +- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/terminal_bsd.go b/terminal_bsd.go index 3b20604f7..c498bcd5f 100644 --- a/terminal_bsd.go +++ b/terminal_bsd.go @@ -3,11 +3,15 @@ package logrus -import "golang.org/x/sys/unix" +import ( + "io" + + "golang.org/x/sys/unix" +) const ioctlReadTermios = unix.TIOCGETA type Termios unix.Termios -func (f *TextFormatter) initTerminal(entry *Entry) { +func initTerminal(w io.Writer) { } diff --git a/terminal_linux.go b/terminal_linux.go index 78c4298dc..6eb013e38 100644 --- a/terminal_linux.go +++ b/terminal_linux.go @@ -7,11 +7,15 @@ package logrus -import "golang.org/x/sys/unix" +import ( + "io" + + "golang.org/x/sys/unix" +) const ioctlReadTermios = unix.TCGETS type Termios unix.Termios -func (f *TextFormatter) initTerminal(entry *Entry) { +func initTerminal(w io.Writer) { } diff --git a/terminal_windows.go b/terminal_windows.go index c9045a017..2494950fc 100644 --- a/terminal_windows.go +++ b/terminal_windows.go @@ -3,17 +3,16 @@ package logrus import ( + "io" "os" "syscall" sequences "github.com/konsorten/go-windows-terminal-sequences" ) -func (f *TextFormatter) initTerminal(entry *Entry) { - switch v := entry.Logger.Out.(type) { +func initTerminal(w io.Writer) { + switch v := w.(type) { case *os.File: - handle := syscall.Handle(v.Fd()) - - sequences.EnableVirtualTerminalProcessing(handle, true) + sequences.EnableVirtualTerminalProcessing(syscall.Handle(v.Fd()), true) } } diff --git a/text_formatter.go b/text_formatter.go index c62c412fc..b4e1f0ac9 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -68,7 +68,7 @@ func (f *TextFormatter) init(entry *Entry) { f.isTerminal = checkIfTerminal(entry.Logger.Out) if f.isTerminal { - f.initTerminal(entry) + initTerminal(entry.Logger.Out) } } } From bfda6de73328b37851743db50a54539c0e90d0b3 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Mon, 16 Apr 2018 14:16:44 +0200 Subject: [PATCH 346/547] Improve documentation for Fatal* class functions --- exported.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/exported.go b/exported.go index 013183eda..dfd982a60 100644 --- a/exported.go +++ b/exported.go @@ -107,7 +107,7 @@ func Panic(args ...interface{}) { std.Panic(args...) } -// Fatal logs a message at level Fatal on the standard logger. +// Fatal logs a message at level Fatal on the standard logger then the process will exit with status set to 1. func Fatal(args ...interface{}) { std.Fatal(args...) } @@ -147,7 +147,7 @@ func Panicf(format string, args ...interface{}) { std.Panicf(format, args...) } -// Fatalf logs a message at level Fatal on the standard logger. +// Fatalf logs a message at level Fatal on the standard logger then the process will exit with status set to 1. func Fatalf(format string, args ...interface{}) { std.Fatalf(format, args...) } @@ -187,7 +187,7 @@ func Panicln(args ...interface{}) { std.Panicln(args...) } -// Fatalln logs a message at level Fatal on the standard logger. +// Fatalln logs a message at level Fatal on the standard logger then the process will exit with status set to 1. func Fatalln(args ...interface{}) { std.Fatalln(args...) } From 572dbe07b4ec650b45521b1093fe9de212876f72 Mon Sep 17 00:00:00 2001 From: dbs5 Date: Mon, 16 Apr 2018 20:20:18 +0200 Subject: [PATCH 347/547] added Anexia CloudLog to list of hooks --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a29222ccd..46a63e5ab 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) | +| [Anexia CloudLog](https://github.com/anexia-it/go-logrus-cloudlog) | Hook for logging to Anexia CloudLog | | [Application Insights](https://github.com/jjcollinge/logrus-appinsights) | Hook for logging to [Application Insights](https://azure.microsoft.com/en-us/services/application-insights/) | [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. | From b96eddc4edbbb89ec584c95ef6dc407c735a271a Mon Sep 17 00:00:00 2001 From: lyric Date: Fri, 27 Apr 2018 17:53:47 +0800 Subject: [PATCH 348/547] add mysql hook --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a29222ccd..33711e41b 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v | [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 | +| [MySQL](https://github.com/LyricTian/logrus-mysql-hook) | Hook for logging to mysql | | [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. | From 3b940ebb1c86de6f88534450b43a4f3e3330e2da Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sat, 12 May 2018 15:51:19 +0200 Subject: [PATCH 349/547] Fix a race condition in TestLoggingWithHooksRace --- hooks/test/test_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hooks/test/test_test.go b/hooks/test/test_test.go index dea768e6c..742be5539 100644 --- a/hooks/test/test_test.go +++ b/hooks/test/test_test.go @@ -51,11 +51,11 @@ func TestLoggingWithHooksRace(t *testing.T) { }() } + wg.Wait() + assert.Equal(logrus.InfoLevel, hook.LastEntry().Level) assert.Equal("info", hook.LastEntry().Message) - wg.Wait() - entries := hook.AllEntries() assert.Equal(100, len(entries)) } From ee38fa072b2aa04105426b96481d1aa9a2ae6685 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sun, 13 May 2018 10:29:45 +0200 Subject: [PATCH 350/547] Update go versions in travis configuration. We only keep the latest 3 language release. Fixes #753 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a23296a53..aebdc3548 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: go go: - - 1.6.x - - 1.7.x - 1.8.x + - 1.9.x + - 1.10.x - tip env: - GOMAXPROCS=4 GORACE=halt_on_error=1 From 0870604a6ab7683bdd746450d60afc58cea9291e Mon Sep 17 00:00:00 2001 From: taylorchu Date: Tue, 15 May 2018 10:07:01 -0700 Subject: [PATCH 351/547] PERF: use buffer pool in json formatter benchmark old ns/op new ns/op delta BenchmarkLogrus-8 4163 4369 +4.95% benchmark old allocs new allocs delta BenchmarkLogrus-8 36 31 -13.89% benchmark old bytes new bytes delta BenchmarkLogrus-8 3027 2163 -28.54% --- json_formatter.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/json_formatter.go b/json_formatter.go index 706494732..82a1da866 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -1,6 +1,7 @@ package logrus import ( + "bytes" "encoding/json" "fmt" ) @@ -71,9 +72,15 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() - serialized, err := json.Marshal(data) + var b *bytes.Buffer + if entry.Buffer != nil { + b = entry.Buffer + } else { + b = &bytes.Buffer{} + } + err := json.NewEncoder(b).Encode(data) if err != nil { return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) } - return append(serialized, '\n'), nil + return b.Bytes(), nil } From 2b7cf918c8d0230707ebce069528f8c7c5e23ce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=87o?= Date: Thu, 17 May 2018 11:02:39 +0200 Subject: [PATCH 352/547] Fix Logger.WithField doscription I was puzzled by function documentation not mentioning it works with Error level, so I had to check it out by creating example before I add logrus as a dependency on the company project. Example confirmed what logic was telling me that Logger.WithFields works with Error level of logs. This is is a fix of this small documentation oversight. --- logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logger.go b/logger.go index fdaf8a653..0ac8ce281 100644 --- a/logger.go +++ b/logger.go @@ -88,7 +88,7 @@ func (logger *Logger) releaseEntry(entry *Entry) { } // Adds a field to the log entry, note that it doesn't log until you call -// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry. +// Debug, Print, Info, Warn, Error, Fatal or Panic. It only creates a log entry. // If you want multiple fields, use `WithFields`. func (logger *Logger) WithField(key string, value interface{}) *Entry { entry := logger.newEntry() From b3067b25afd3c440665f30bf4aa7da41fa01f821 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sat, 19 May 2018 21:26:32 +0200 Subject: [PATCH 353/547] Move the hook services list to a wiki page --- README.md | 58 ++----------------------------------------------------- 1 file changed, 2 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 7f8625425..072e99be3 100644 --- a/README.md +++ b/README.md @@ -241,62 +241,8 @@ func init() { ``` Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md). -| Hook | Description | -| ----- | ----------- | -| [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) | -| [Anexia CloudLog](https://github.com/anexia-it/go-logrus-cloudlog) | Hook for logging to Anexia CloudLog | -| [Application Insights](https://github.com/jjcollinge/logrus-appinsights) | Hook for logging to [Application Insights](https://azure.microsoft.com/en-us/services/application-insights/) -| [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. | -| [ClickHouse](https://github.com/oxgrouby/logrus-clickhouse-hook) | Send logs to [ClickHouse](https://clickhouse.yandex/) | -| [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/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/) | -| [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/tracer0tong/kafkalogrus) | Hook for logging to Kafka | -| [Kafka REST Proxy](https://github.com/Nordstrom/logrus-kafka-rest-proxy) | Hook for logging to [Kafka REST Proxy](https://docs.confluent.io/current/kafka-rest/docs) | -| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem | -| [Logbeat](https://github.com/macandmia/logbeat) | Hook for logging to [Opbeat](https://opbeat.com/) | -| [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/) | -| [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 | -| [MySQL](https://github.com/LyricTian/logrus-mysql-hook) | Hook for logging to mysql | -| [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) | -| [Promrus](https://github.com/weaveworks/promrus) | Expose number of log messages as [Prometheus](https://prometheus.io/) metrics | -| [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. | -| [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. | -| [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/) | -| [SQS-Hook](https://github.com/tsarpaul/logrus_sqs) | Hook for logging to [Amazon Simple Queue Service (SQS)](https://aws.amazon.com/sqs/) | +A list of currently known of service hook can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks) + #### Level logging From ec58181d62d1fdfd6bfca0d502c9e454a7adcff3 Mon Sep 17 00:00:00 2001 From: Moriyoshi Koizumi Date: Wed, 30 May 2018 09:50:59 +0000 Subject: [PATCH 354/547] Revert the change introduced in #707 and do the proper fix. Fixes #729 --- entry.go | 6 ++---- hooks/test/test.go | 15 ++++++--------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/entry.go b/entry.go index d075d7237..1be48ab09 100644 --- a/entry.go +++ b/entry.go @@ -113,12 +113,10 @@ func (entry Entry) log(level Level, msg string) { } } -// This function is not declared with a pointer value because otherwise -// race conditions will occur when using multiple goroutines -func (entry Entry) fireHooks() { +func (entry *Entry) fireHooks() { entry.Logger.mu.Lock() defer entry.Logger.mu.Unlock() - err := entry.Logger.Hooks.Fire(entry.Level, &entry) + err := entry.Logger.Hooks.Fire(entry.Level, entry) if err != nil { fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) } diff --git a/hooks/test/test.go b/hooks/test/test.go index 62c4845df..234a17dfa 100644 --- a/hooks/test/test.go +++ b/hooks/test/test.go @@ -15,7 +15,7 @@ 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 + Entries []logrus.Entry mu sync.RWMutex } @@ -52,7 +52,7 @@ 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) + t.Entries = append(t.Entries, *e) return nil } @@ -68,9 +68,7 @@ func (t *Hook) LastEntry() *logrus.Entry { if i < 0 { return nil } - // Make a copy, for safety - e := *t.Entries[i] - return &e + return &t.Entries[i] } // AllEntries returns all entries that were logged. @@ -79,10 +77,9 @@ func (t *Hook) AllEntries() []*logrus.Entry { 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 { + for i := 0; i < len(t.Entries); i++ { // Make a copy, for safety - e := *entry - entries[i] = &e + entries[i] = &t.Entries[i] } return entries } @@ -91,5 +88,5 @@ func (t *Hook) AllEntries() []*logrus.Entry { func (t *Hook) Reset() { t.mu.Lock() defer t.mu.Unlock() - t.Entries = make([]*logrus.Entry, 0) + t.Entries = make([]logrus.Entry, 0) } From c1fb0b890f30250c5fd89b672d9af3dd9670c250 Mon Sep 17 00:00:00 2001 From: Logan HAUSPIE Date: Wed, 30 May 2018 01:47:39 +0200 Subject: [PATCH 355/547] feat: new methods to check enabled log level Adding 6 methods on 'exported', 'logger' and 'entry': - IsDebugEnabled() bool - IsInfoEnabled() bool - IsWarnEnabled() bool - IsErrorEnabled() bool - IsFatalEnabled() bool - IsPanicEnabled() bool Replace duplicated 'if logger.level() >= XxxxLevel' by a call to the new methods in 'logger' and 'entry' Closes #761 --- entry.go | 60 +++++++++++++++++++++++++++++++-------------- exported.go | 24 ++++++++++++++++++ logger.go | 66 ++++++++++++++++++++++++++++++++++---------------- logrus.go | 7 ++++++ logrus_test.go | 51 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 169 insertions(+), 39 deletions(-) diff --git a/entry.go b/entry.go index d075d7237..29bcae64f 100644 --- a/entry.go +++ b/entry.go @@ -139,7 +139,7 @@ func (entry *Entry) write() { } func (entry *Entry) Debug(args ...interface{}) { - if entry.Logger.level() >= DebugLevel { + if entry.IsDebugEnabled() { entry.log(DebugLevel, fmt.Sprint(args...)) } } @@ -149,13 +149,13 @@ func (entry *Entry) Print(args ...interface{}) { } func (entry *Entry) Info(args ...interface{}) { - if entry.Logger.level() >= InfoLevel { + if entry.IsInfoEnabled() { entry.log(InfoLevel, fmt.Sprint(args...)) } } func (entry *Entry) Warn(args ...interface{}) { - if entry.Logger.level() >= WarnLevel { + if entry.IsWarnEnabled() { entry.log(WarnLevel, fmt.Sprint(args...)) } } @@ -165,20 +165,20 @@ func (entry *Entry) Warning(args ...interface{}) { } func (entry *Entry) Error(args ...interface{}) { - if entry.Logger.level() >= ErrorLevel { + if entry.IsErrorEnabled() { entry.log(ErrorLevel, fmt.Sprint(args...)) } } func (entry *Entry) Fatal(args ...interface{}) { - if entry.Logger.level() >= FatalLevel { + if entry.IsFatalEnabled() { entry.log(FatalLevel, fmt.Sprint(args...)) } Exit(1) } func (entry *Entry) Panic(args ...interface{}) { - if entry.Logger.level() >= PanicLevel { + if entry.IsPanicEnabled() { entry.log(PanicLevel, fmt.Sprint(args...)) } panic(fmt.Sprint(args...)) @@ -187,13 +187,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.IsDebugEnabled() { entry.Debug(fmt.Sprintf(format, args...)) } } func (entry *Entry) Infof(format string, args ...interface{}) { - if entry.Logger.level() >= InfoLevel { + if entry.IsInfoEnabled() { entry.Info(fmt.Sprintf(format, args...)) } } @@ -203,7 +203,7 @@ func (entry *Entry) Printf(format string, args ...interface{}) { } func (entry *Entry) Warnf(format string, args ...interface{}) { - if entry.Logger.level() >= WarnLevel { + if entry.IsWarnEnabled() { entry.Warn(fmt.Sprintf(format, args...)) } } @@ -213,20 +213,20 @@ func (entry *Entry) Warningf(format string, args ...interface{}) { } func (entry *Entry) Errorf(format string, args ...interface{}) { - if entry.Logger.level() >= ErrorLevel { + if entry.IsErrorEnabled() { entry.Error(fmt.Sprintf(format, args...)) } } func (entry *Entry) Fatalf(format string, args ...interface{}) { - if entry.Logger.level() >= FatalLevel { + if entry.IsFatalEnabled() { entry.Fatal(fmt.Sprintf(format, args...)) } Exit(1) } func (entry *Entry) Panicf(format string, args ...interface{}) { - if entry.Logger.level() >= PanicLevel { + if entry.IsPanicEnabled() { entry.Panic(fmt.Sprintf(format, args...)) } } @@ -234,13 +234,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.IsDebugEnabled() { entry.Debug(entry.sprintlnn(args...)) } } func (entry *Entry) Infoln(args ...interface{}) { - if entry.Logger.level() >= InfoLevel { + if entry.IsInfoEnabled() { entry.Info(entry.sprintlnn(args...)) } } @@ -250,7 +250,7 @@ func (entry *Entry) Println(args ...interface{}) { } func (entry *Entry) Warnln(args ...interface{}) { - if entry.Logger.level() >= WarnLevel { + if entry.IsWarnEnabled() { entry.Warn(entry.sprintlnn(args...)) } } @@ -260,24 +260,48 @@ func (entry *Entry) Warningln(args ...interface{}) { } func (entry *Entry) Errorln(args ...interface{}) { - if entry.Logger.level() >= ErrorLevel { + if entry.IsErrorEnabled() { entry.Error(entry.sprintlnn(args...)) } } func (entry *Entry) Fatalln(args ...interface{}) { - if entry.Logger.level() >= FatalLevel { + if entry.IsFatalEnabled() { entry.Fatal(entry.sprintlnn(args...)) } Exit(1) } func (entry *Entry) Panicln(args ...interface{}) { - if entry.Logger.level() >= PanicLevel { + if entry.IsPanicEnabled() { entry.Panic(entry.sprintlnn(args...)) } } +func (entry *Entry) IsDebugEnabled() bool { + return entry.Logger.IsDebugEnabled() +} + +func (entry *Entry) IsInfoEnabled() bool { + return entry.Logger.IsInfoEnabled() +} + +func (entry *Entry) IsWarnEnabled() bool { + return entry.Logger.IsWarnEnabled() +} + +func (entry *Entry) IsErrorEnabled() bool { + return entry.Logger.IsErrorEnabled() +} + +func (entry *Entry) IsFatalEnabled() bool { + return entry.Logger.IsFatalEnabled() +} + +func (entry *Entry) IsPanicEnabled() bool { + return entry.Logger.IsPanicEnabled() +} + // Sprintlnn => Sprint no newline. This is to get the behavior of how // fmt.Sprintln where spaces are always added between operands, regardless of // their type. Instead of vendoring the Sprintln implementation to spare a diff --git a/exported.go b/exported.go index 013183eda..f2df9e321 100644 --- a/exported.go +++ b/exported.go @@ -41,6 +41,30 @@ func GetLevel() Level { return std.level() } +func IsDebugEnabled() bool { + return std.IsDebugEnabled() +} + +func IsInfoEnabled() bool { + return std.IsInfoEnabled() +} + +func IsWarnEnabled() bool { + return std.IsWarnEnabled() +} + +func IsErrorEnabled() bool { + return std.IsErrorEnabled() +} + +func IsFatalEnabled() bool { + return std.IsFatalEnabled() +} + +func IsPanicEnabled() bool { + return std.IsPanicEnabled() +} + // AddHook adds a hook to the standard logger hooks. func AddHook(hook Hook) { std.mu.Lock() diff --git a/logger.go b/logger.go index fdaf8a653..034fb1876 100644 --- a/logger.go +++ b/logger.go @@ -113,7 +113,7 @@ func (logger *Logger) WithError(err error) *Entry { } func (logger *Logger) Debugf(format string, args ...interface{}) { - if logger.level() >= DebugLevel { + if logger.IsDebugEnabled() { entry := logger.newEntry() entry.Debugf(format, args...) logger.releaseEntry(entry) @@ -121,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.IsInfoEnabled() { entry := logger.newEntry() entry.Infof(format, args...) logger.releaseEntry(entry) @@ -135,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.IsWarnEnabled() { entry := logger.newEntry() entry.Warnf(format, args...) logger.releaseEntry(entry) @@ -143,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.IsWarnEnabled() { entry := logger.newEntry() entry.Warnf(format, args...) logger.releaseEntry(entry) @@ -151,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.IsErrorEnabled() { entry := logger.newEntry() entry.Errorf(format, args...) logger.releaseEntry(entry) @@ -159,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.IsFatalEnabled() { entry := logger.newEntry() entry.Fatalf(format, args...) logger.releaseEntry(entry) @@ -168,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.IsPanicEnabled() { entry := logger.newEntry() entry.Panicf(format, args...) logger.releaseEntry(entry) @@ -176,7 +176,7 @@ func (logger *Logger) Panicf(format string, args ...interface{}) { } func (logger *Logger) Debug(args ...interface{}) { - if logger.level() >= DebugLevel { + if logger.IsDebugEnabled() { entry := logger.newEntry() entry.Debug(args...) logger.releaseEntry(entry) @@ -184,7 +184,7 @@ func (logger *Logger) Debug(args ...interface{}) { } func (logger *Logger) Info(args ...interface{}) { - if logger.level() >= InfoLevel { + if logger.IsInfoEnabled() { entry := logger.newEntry() entry.Info(args...) logger.releaseEntry(entry) @@ -198,7 +198,7 @@ func (logger *Logger) Print(args ...interface{}) { } func (logger *Logger) Warn(args ...interface{}) { - if logger.level() >= WarnLevel { + if logger.IsWarnEnabled() { entry := logger.newEntry() entry.Warn(args...) logger.releaseEntry(entry) @@ -206,7 +206,7 @@ func (logger *Logger) Warn(args ...interface{}) { } func (logger *Logger) Warning(args ...interface{}) { - if logger.level() >= WarnLevel { + if logger.IsWarnEnabled() { entry := logger.newEntry() entry.Warn(args...) logger.releaseEntry(entry) @@ -214,7 +214,7 @@ func (logger *Logger) Warning(args ...interface{}) { } func (logger *Logger) Error(args ...interface{}) { - if logger.level() >= ErrorLevel { + if logger.IsErrorEnabled() { entry := logger.newEntry() entry.Error(args...) logger.releaseEntry(entry) @@ -222,7 +222,7 @@ func (logger *Logger) Error(args ...interface{}) { } func (logger *Logger) Fatal(args ...interface{}) { - if logger.level() >= FatalLevel { + if logger.IsFatalEnabled() { entry := logger.newEntry() entry.Fatal(args...) logger.releaseEntry(entry) @@ -231,7 +231,7 @@ func (logger *Logger) Fatal(args ...interface{}) { } func (logger *Logger) Panic(args ...interface{}) { - if logger.level() >= PanicLevel { + if logger.IsPanicEnabled() { entry := logger.newEntry() entry.Panic(args...) logger.releaseEntry(entry) @@ -239,7 +239,7 @@ func (logger *Logger) Panic(args ...interface{}) { } func (logger *Logger) Debugln(args ...interface{}) { - if logger.level() >= DebugLevel { + if logger.IsDebugEnabled() { entry := logger.newEntry() entry.Debugln(args...) logger.releaseEntry(entry) @@ -247,7 +247,7 @@ func (logger *Logger) Debugln(args ...interface{}) { } func (logger *Logger) Infoln(args ...interface{}) { - if logger.level() >= InfoLevel { + if logger.IsInfoEnabled() { entry := logger.newEntry() entry.Infoln(args...) logger.releaseEntry(entry) @@ -261,7 +261,7 @@ func (logger *Logger) Println(args ...interface{}) { } func (logger *Logger) Warnln(args ...interface{}) { - if logger.level() >= WarnLevel { + if logger.IsWarnEnabled() { entry := logger.newEntry() entry.Warnln(args...) logger.releaseEntry(entry) @@ -269,7 +269,7 @@ func (logger *Logger) Warnln(args ...interface{}) { } func (logger *Logger) Warningln(args ...interface{}) { - if logger.level() >= WarnLevel { + if logger.IsWarnEnabled() { entry := logger.newEntry() entry.Warnln(args...) logger.releaseEntry(entry) @@ -277,7 +277,7 @@ func (logger *Logger) Warningln(args ...interface{}) { } func (logger *Logger) Errorln(args ...interface{}) { - if logger.level() >= ErrorLevel { + if logger.IsErrorEnabled() { entry := logger.newEntry() entry.Errorln(args...) logger.releaseEntry(entry) @@ -285,7 +285,7 @@ func (logger *Logger) Errorln(args ...interface{}) { } func (logger *Logger) Fatalln(args ...interface{}) { - if logger.level() >= FatalLevel { + if logger.IsFatalEnabled() { entry := logger.newEntry() entry.Fatalln(args...) logger.releaseEntry(entry) @@ -294,7 +294,7 @@ func (logger *Logger) Fatalln(args ...interface{}) { } func (logger *Logger) Panicln(args ...interface{}) { - if logger.level() >= PanicLevel { + if logger.IsPanicEnabled() { entry := logger.newEntry() entry.Panicln(args...) logger.releaseEntry(entry) @@ -321,3 +321,27 @@ func (logger *Logger) AddHook(hook Hook) { defer logger.mu.Unlock() logger.Hooks.Add(hook) } + +func (logger *Logger) IsDebugEnabled() bool { + return logger.level() >= DebugLevel +} + +func (logger *Logger) IsInfoEnabled() bool { + return logger.level() >= InfoLevel +} + +func (logger *Logger) IsWarnEnabled() bool { + return logger.level() >= WarnLevel +} + +func (logger *Logger) IsErrorEnabled() bool { + return logger.level() >= ErrorLevel +} + +func (logger *Logger) IsFatalEnabled() bool { + return logger.level() >= FatalLevel +} + +func (logger *Logger) IsPanicEnabled() bool { + return logger.level() >= PanicLevel +} diff --git a/logrus.go b/logrus.go index dd3899974..33e07fa0e 100644 --- a/logrus.go +++ b/logrus.go @@ -140,4 +140,11 @@ type FieldLogger interface { Errorln(args ...interface{}) Fatalln(args ...interface{}) Panicln(args ...interface{}) + + IsDebugEnabled() bool + IsInfoEnabled() bool + IsWarnEnabled() bool + IsErrorEnabled() bool + IsFatalEnabled() bool + IsPanicEnabled() bool } diff --git a/logrus_test.go b/logrus_test.go index 78cbc2825..2c59dc90e 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -384,3 +384,54 @@ func TestEntryWriter(t *testing.T) { assert.Equal(t, fields["foo"], "bar") assert.Equal(t, fields["level"], "warning") } + +func TestLogLevelEnabled(t *testing.T) { + log := New() + log.SetLevel(PanicLevel) + assert.Equal(t, true, log.IsPanicEnabled()) + assert.Equal(t, false, log.IsFatalEnabled()) + assert.Equal(t, false, log.IsErrorEnabled()) + assert.Equal(t, false, log.IsWarnEnabled()) + assert.Equal(t, false, log.IsInfoEnabled()) + assert.Equal(t, false, log.IsDebugEnabled()) + + log.SetLevel(FatalLevel) + assert.Equal(t, true, log.IsPanicEnabled()) + assert.Equal(t, true, log.IsFatalEnabled()) + assert.Equal(t, false, log.IsErrorEnabled()) + assert.Equal(t, false, log.IsWarnEnabled()) + assert.Equal(t, false, log.IsInfoEnabled()) + assert.Equal(t, false, log.IsDebugEnabled()) + + log.SetLevel(ErrorLevel) + assert.Equal(t, true, log.IsPanicEnabled()) + assert.Equal(t, true, log.IsFatalEnabled()) + assert.Equal(t, true, log.IsErrorEnabled()) + assert.Equal(t, false, log.IsWarnEnabled()) + assert.Equal(t, false, log.IsInfoEnabled()) + assert.Equal(t, false, log.IsDebugEnabled()) + + log.SetLevel(WarnLevel) + assert.Equal(t, true, log.IsPanicEnabled()) + assert.Equal(t, true, log.IsFatalEnabled()) + assert.Equal(t, true, log.IsErrorEnabled()) + assert.Equal(t, true, log.IsWarnEnabled()) + assert.Equal(t, false, log.IsInfoEnabled()) + assert.Equal(t, false, log.IsDebugEnabled()) + + log.SetLevel(InfoLevel) + assert.Equal(t, true, log.IsPanicEnabled()) + assert.Equal(t, true, log.IsFatalEnabled()) + assert.Equal(t, true, log.IsErrorEnabled()) + assert.Equal(t, true, log.IsWarnEnabled()) + assert.Equal(t, true, log.IsInfoEnabled()) + assert.Equal(t, false, log.IsDebugEnabled()) + + log.SetLevel(DebugLevel) + assert.Equal(t, true, log.IsPanicEnabled()) + assert.Equal(t, true, log.IsFatalEnabled()) + assert.Equal(t, true, log.IsErrorEnabled()) + assert.Equal(t, true, log.IsWarnEnabled()) + assert.Equal(t, true, log.IsInfoEnabled()) + assert.Equal(t, true, log.IsDebugEnabled()) +} From 1fd13a9bb963d16f7c3c8ea20940cc9e2feb1b01 Mon Sep 17 00:00:00 2001 From: Neil Isaac Date: Mon, 18 Jun 2018 21:32:35 -0400 Subject: [PATCH 356/547] Fixed prefixFieldClashes for TextFormatter and added coverage --- formatter.go | 3 +++ text_formatter.go | 8 ++++---- text_formatter_test.go | 14 +++++++++++++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/formatter.go b/formatter.go index 849dc8b97..83c74947b 100644 --- a/formatter.go +++ b/formatter.go @@ -34,15 +34,18 @@ func prefixFieldClashes(data Fields, fieldMap FieldMap) { timeKey := fieldMap.resolve(FieldKeyTime) if t, ok := data[timeKey]; ok { data["fields."+timeKey] = t + delete(data, timeKey) } msgKey := fieldMap.resolve(FieldKeyMsg) if m, ok := data[msgKey]; ok { data["fields."+msgKey] = m + delete(data, msgKey) } levelKey := fieldMap.resolve(FieldKeyLevel) if l, ok := data[levelKey]; ok { data["fields."+levelKey] = l + delete(data, levelKey) } } diff --git a/text_formatter.go b/text_formatter.go index 5af4e5622..3e5504030 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -51,7 +51,6 @@ type TextFormatter struct { // be desired. DisableSorting bool - // Disables the truncation of the level text to 4 characters. DisableLevelTruncation bool @@ -81,7 +80,8 @@ func (f *TextFormatter) init(entry *Entry) { // Format renders a single log entry func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { - var b *bytes.Buffer + prefixFieldClashes(entry.Data, f.FieldMap) + keys := make([]string, 0, len(entry.Data)) for k := range entry.Data { keys = append(keys, k) @@ -90,14 +90,14 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { if !f.DisableSorting { sort.Strings(keys) } + + var b *bytes.Buffer if entry.Buffer != nil { b = entry.Buffer } else { b = &bytes.Buffer{} } - prefixFieldClashes(entry.Data, emptyFieldMap) - f.Do(func() { f.init(entry) }) isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors diff --git a/text_formatter_test.go b/text_formatter_test.go index 4f2186106..7245f9454 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -191,6 +191,12 @@ func TestTextFormatterFieldMap(t *testing.T) { Message: "oh hi", Level: WarnLevel, Time: time.Date(1981, time.February, 24, 4, 28, 3, 100, time.UTC), + Data: Fields{ + "field1": "f1", + "message": "messagefield", + "somelevel": "levelfield", + "timeywimey": "timeywimeyfield", + }, } b, err := formatter.Format(entry) @@ -199,7 +205,13 @@ func TestTextFormatterFieldMap(t *testing.T) { } assert.Equal(t, - `timeywimey="1981-02-24T04:28:03Z" somelevel=warning message="oh hi"`+"\n", + `timeywimey="1981-02-24T04:28:03Z" `+ + `somelevel=warning `+ + `message="oh hi" `+ + `field1=f1 `+ + `fields.message=messagefield `+ + `fields.somelevel=levelfield `+ + `fields.timeywimey=timeywimeyfield`+"\n", string(b), "Formatted doesn't respect correct FieldMap") } From 6be931b243e39774a6906abd785f8fddde9e16a6 Mon Sep 17 00:00:00 2001 From: Neil Isaac Date: Mon, 18 Jun 2018 21:39:53 -0400 Subject: [PATCH 357/547] error message --- text_formatter_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text_formatter_test.go b/text_formatter_test.go index 7245f9454..921d0522b 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -213,7 +213,7 @@ func TestTextFormatterFieldMap(t *testing.T) { `fields.somelevel=levelfield `+ `fields.timeywimey=timeywimeyfield`+"\n", string(b), - "Formatted doesn't respect correct FieldMap") + "Formatted output doesn't respect FieldMap") } // TODO add tests for sorting etc., this requires a parser for the text From 23a8b4607ea67a67d53980ea397f2c23f135f12d Mon Sep 17 00:00:00 2001 From: Przemyslaw Wegrzyn Date: Tue, 19 Jun 2018 14:31:57 +0200 Subject: [PATCH 358/547] Support for Entry data under nested JSON dictionary. --- json_formatter.go | 10 ++++++++++ json_formatter_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/json_formatter.go b/json_formatter.go index 706494732..dab17610f 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -33,6 +33,9 @@ type JSONFormatter struct { // DisableTimestamp allows disabling automatic timestamps in output DisableTimestamp bool + // DataKey allows users to put all the log entry parameters into a nested dictionary at a given key. + DataKey string + // FieldMap allows users to customize the names of keys for default fields. // As an example: // formatter := &JSONFormatter{ @@ -58,6 +61,13 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { data[k] = v } } + + if f.DataKey != "" { + newData := make(Fields, 4) + newData[f.DataKey] = data + data = newData + } + prefixFieldClashes(data, f.FieldMap) timestampFormat := f.TimestampFormat diff --git a/json_formatter_test.go b/json_formatter_test.go index 1c140d045..0dde3004c 100644 --- a/json_formatter_test.go +++ b/json_formatter_test.go @@ -161,6 +161,48 @@ func TestFieldClashWithRemappedFields(t *testing.T) { } } +func TestFieldsInNestedDictionary(t *testing.T) { + formatter := &JSONFormatter{ + DataKey: "args", + } + + logEntry := WithFields(Fields{ + "level": "level", + "test": "test", + }) + logEntry.Level = InfoLevel + + b, err := formatter.Format(logEntry) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + + entry := make(map[string]interface{}) + err = json.Unmarshal(b, &entry) + if err != nil { + t.Fatal("Unable to unmarshal formatted entry: ", err) + } + + args := entry["args"].(map[string]interface{}) + + for _, field := range []string{"test", "level"} { + if value, present := args[field]; !present || value != field { + t.Errorf("Expected field %v to be present under 'args'; untouched", field) + } + } + + for _, field := range []string{"test", "fields.level"} { + if _, present := entry[field]; present { + t.Errorf("Expected field %v not to be present at top level", field) + } + } + + // with nested object, "level" shouldn't clash + if entry["level"] != "info" { + t.Errorf("Expected 'level' field to contain 'info'") + } +} + func TestJSONEntryEndsWithNewline(t *testing.T) { formatter := &JSONFormatter{} From 692b80a4bb6960364e54de1d338f5eefbab5cdd6 Mon Sep 17 00:00:00 2001 From: Christian Stewart Date: Wed, 20 Jun 2018 21:39:23 -0700 Subject: [PATCH 359/547] Fix GopherJS build tags The GopherJS build tag is "js" not "gopherjs" Signed-off-by: Christian Stewart --- terminal_bsd.go | 2 +- terminal_check_appengine.go | 2 +- terminal_check_notappengine.go | 2 +- terminal_linux.go | 2 +- text_formatter_js.go | 11 +++++++++++ text_formatter_other.go | 3 +++ 6 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 text_formatter_js.go create mode 100644 text_formatter_other.go diff --git a/terminal_bsd.go b/terminal_bsd.go index 4880d13d2..5b6212d24 100644 --- a/terminal_bsd.go +++ b/terminal_bsd.go @@ -1,5 +1,5 @@ // +build darwin freebsd openbsd netbsd dragonfly -// +build !appengine,!gopherjs +// +build !appengine,!js package logrus diff --git a/terminal_check_appengine.go b/terminal_check_appengine.go index 3de08e802..26a2867a7 100644 --- a/terminal_check_appengine.go +++ b/terminal_check_appengine.go @@ -1,4 +1,4 @@ -// +build appengine gopherjs +// +build appengine js package logrus diff --git a/terminal_check_notappengine.go b/terminal_check_notappengine.go index 067047a12..87f0b8097 100644 --- a/terminal_check_notappengine.go +++ b/terminal_check_notappengine.go @@ -1,4 +1,4 @@ -// +build !appengine,!gopherjs +// +build !appengine,!js package logrus diff --git a/terminal_linux.go b/terminal_linux.go index f29a0097c..634e39b30 100644 --- a/terminal_linux.go +++ b/terminal_linux.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 !appengine,!gopherjs +// +build !appengine,!js package logrus diff --git a/text_formatter_js.go b/text_formatter_js.go new file mode 100644 index 000000000..d52803a69 --- /dev/null +++ b/text_formatter_js.go @@ -0,0 +1,11 @@ +// +build js + +package logrus + +import ( + "io" +) + +func (f *TextFormatter) checkIfTerminal(w io.Writer) bool { + return false +} diff --git a/text_formatter_other.go b/text_formatter_other.go new file mode 100644 index 000000000..0d9704fc3 --- /dev/null +++ b/text_formatter_other.go @@ -0,0 +1,3 @@ +// +build !js + +package logrus From eb6d2e2dd50a87215bd6cbef19a6860988be72ca Mon Sep 17 00:00:00 2001 From: Daniel Bershatsky Date: Wed, 27 Jun 2018 20:29:28 +0300 Subject: [PATCH 360/547] [#241] Allow to set writer during logger usage. --- exported.go | 4 +--- logger.go | 6 ++++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/exported.go b/exported.go index 013183eda..6c98afdc0 100644 --- a/exported.go +++ b/exported.go @@ -15,9 +15,7 @@ func StandardLogger() *Logger { // SetOutput sets the standard logger output. func SetOutput(out io.Writer) { - std.mu.Lock() - defer std.mu.Unlock() - std.Out = out + std.SetOutput(out) } // SetFormatter sets the standard logger formatter. diff --git a/logger.go b/logger.go index 0ac8ce281..0c1b05e63 100644 --- a/logger.go +++ b/logger.go @@ -316,6 +316,12 @@ func (logger *Logger) SetLevel(level Level) { atomic.StoreUint32((*uint32)(&logger.Level), uint32(level)) } +func (logger *Logger) SetOutput(out io.Writer) { + logger.mu.Lock() + defer logger.mu.Unlock() + logger.Out = out +} + func (logger *Logger) AddHook(hook Hook) { logger.mu.Lock() defer logger.mu.Unlock() From b03a8031fd3b2aa41dbe0fdf8fca1660a973ac2f Mon Sep 17 00:00:00 2001 From: Simon Brisson Date: Thu, 28 Jun 2018 16:33:52 -0400 Subject: [PATCH 361/547] Allows overriding Entry.Time. --- entry.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/entry.go b/entry.go index d075d7237..c3adf0100 100644 --- a/entry.go +++ b/entry.go @@ -90,7 +90,16 @@ func (entry *Entry) WithFields(fields Fields) *Entry { // race conditions will occur when using multiple goroutines func (entry Entry) log(level Level, msg string) { var buffer *bytes.Buffer - entry.Time = time.Now() + + // Default to now, but allow users to override if they want. + // + // We don't have to worry about polluting future calls to Entry#log() + // with this assignment because this function is declared with a + // non-pointer receiver. + if entry.Time.IsZero() { + entry.Time = time.Now() + } + entry.Level = level entry.Message = msg From 83509d2b195913e30622c10a192c4088aaf6c9e0 Mon Sep 17 00:00:00 2001 From: Simon Brisson Date: Fri, 29 Jun 2018 10:53:51 -0400 Subject: [PATCH 362/547] Adds WithTime to Logger and Entry types, as well as a pure module-level function. --- entry.go | 7 +++++- exported.go | 10 +++++++++ logger.go | 8 +++++++ logrus_test.go | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 1 deletion(-) diff --git a/entry.go b/entry.go index c3adf0100..14f0a263b 100644 --- a/entry.go +++ b/entry.go @@ -83,7 +83,12 @@ func (entry *Entry) WithFields(fields Fields) *Entry { for k, v := range fields { data[k] = v } - return &Entry{Logger: entry.Logger, Data: data} + return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time} +} + +// Overrides the time of the Entry. +func (entry *Entry) WithTime(t time.Time) *Entry { + return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t} } // This function is not declared with a pointer value because otherwise diff --git a/exported.go b/exported.go index 013183eda..ec1a417e2 100644 --- a/exported.go +++ b/exported.go @@ -2,6 +2,7 @@ package logrus import ( "io" + "time" ) var ( @@ -72,6 +73,15 @@ func WithFields(fields Fields) *Entry { return std.WithFields(fields) } +// WithTime creats an entry from the standard logger and overrides the time of +// logs generated with it. +// +// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal +// or Panic on the Entry it returns. +func WithTime(t time.Time) *Entry { + return std.WithTime(t) +} + // Debug logs a message at level Debug on the standard logger. func Debug(args ...interface{}) { std.Debug(args...) diff --git a/logger.go b/logger.go index 0ac8ce281..52b942d52 100644 --- a/logger.go +++ b/logger.go @@ -5,6 +5,7 @@ import ( "os" "sync" "sync/atomic" + "time" ) type Logger struct { @@ -112,6 +113,13 @@ func (logger *Logger) WithError(err error) *Entry { return entry.WithError(err) } +// Overrides the time of the log entry. +func (logger *Logger) WithTime(t time.Time) *Entry { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + return entry.WithTime(t) +} + func (logger *Logger) Debugf(format string, args ...interface{}) { if logger.level() >= DebugLevel { entry := logger.newEntry() diff --git a/logrus_test.go b/logrus_test.go index 78cbc2825..78e13018c 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -7,6 +7,7 @@ import ( "strings" "sync" "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -209,6 +210,65 @@ func TestDefaultFieldsAreNotPrefixed(t *testing.T) { }) } +func TestWithTimeShouldOverrideTime(t *testing.T) { + now := time.Now().Add(24 * time.Hour) + + LogAndAssertJSON(t, func(log *Logger) { + log.WithTime(now).Info("foobar") + }, func(fields Fields) { + assert.Equal(t, fields["time"], now.Format(defaultTimestampFormat)) + }) +} + +func TestWithTimeShouldNotOverrideFields(t *testing.T) { + now := time.Now().Add(24 * time.Hour) + + LogAndAssertJSON(t, func(log *Logger) { + log.WithField("herp", "derp").WithTime(now).Info("blah") + }, func(fields Fields) { + assert.Equal(t, fields["time"], now.Format(defaultTimestampFormat)) + assert.Equal(t, fields["herp"], "derp") + }) +} + +func TestWithFieldShouldNotOverrideTime(t *testing.T) { + now := time.Now().Add(24 * time.Hour) + + LogAndAssertJSON(t, func(log *Logger) { + log.WithTime(now).WithField("herp", "derp").Info("blah") + }, func(fields Fields) { + assert.Equal(t, fields["time"], now.Format(defaultTimestampFormat)) + assert.Equal(t, fields["herp"], "derp") + }) +} + +func TestTimeOverrideMultipleLogs(t *testing.T) { + var buffer bytes.Buffer + var firstFields, secondFields Fields + + logger := New() + logger.Out = &buffer + formatter := new(JSONFormatter) + formatter.TimestampFormat = time.StampMilli + logger.Formatter = formatter + + llog := logger.WithField("herp", "derp") + llog.Info("foo") + + err := json.Unmarshal(buffer.Bytes(), &firstFields) + assert.NoError(t, err, "should have decoded first message") + + buffer.Reset() + + time.Sleep(10 * time.Millisecond) + llog.Info("bar") + + err = json.Unmarshal(buffer.Bytes(), &secondFields) + assert.NoError(t, err, "should have decoded second message") + + assert.NotEqual(t, firstFields["time"], secondFields["time"], "timestamps should not be equal") +} + func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) { var buffer bytes.Buffer From f338880618fc65bd81ab6882e06e0f03981b6f62 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Fri, 20 Jul 2018 13:16:19 +0200 Subject: [PATCH 363/547] properly fix the hooks race test --- hooks/test/test_test.go | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/hooks/test/test_test.go b/hooks/test/test_test.go index 742be5539..d6f6d306d 100644 --- a/hooks/test/test_test.go +++ b/hooks/test/test_test.go @@ -1,8 +1,10 @@ package test import ( + "math/rand" "sync" "testing" + "time" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" @@ -38,24 +40,34 @@ func TestAllHooks(t *testing.T) { } func TestLoggingWithHooksRace(t *testing.T) { + + rand.Seed(time.Now().Unix()) + unlocker := rand.Int() % 100 + assert := assert.New(t) logger, hook := NewNullLogger() - var wg sync.WaitGroup - wg.Add(100) + var wgOne, wgAll sync.WaitGroup + wgOne.Add(1) + wgAll.Add(100) for i := 0; i < 100; i++ { - go func() { + go func(i int) { logger.Info("info") - wg.Done() - }() + wgAll.Done() + if i == unlocker { + wgOne.Done() + } + }(i) } - wg.Wait() + wgOne.Wait() assert.Equal(logrus.InfoLevel, hook.LastEntry().Level) assert.Equal("info", hook.LastEntry().Message) + wgAll.Wait() + entries := hook.AllEntries() assert.Equal(100, len(entries)) } From eaf95baa693dac7223610311439532f57eb887cb Mon Sep 17 00:00:00 2001 From: David Bariod Date: Fri, 20 Jul 2018 13:34:26 +0200 Subject: [PATCH 364/547] limit the build/test matrix to the two latest stable version --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index aebdc3548..2f19b4a75 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,7 @@ language: go go: - - 1.8.x - 1.9.x - 1.10.x - - tip env: - GOMAXPROCS=4 GORACE=halt_on_error=1 install: From 4e7461bffe33730c133a47b7bcf4d8beb207d33a Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sat, 28 Jul 2018 17:21:06 +0200 Subject: [PATCH 365/547] Add logger benchmark --- logger_bench_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/logger_bench_test.go b/logger_bench_test.go index dd23a3535..f0a768439 100644 --- a/logger_bench_test.go +++ b/logger_bench_test.go @@ -1,6 +1,7 @@ package logrus import ( + "io/ioutil" "os" "testing" ) @@ -59,3 +60,26 @@ func doLoggerBenchmarkNoLock(b *testing.B, out *os.File, formatter Formatter, fi } }) } + +func BenchmarkLoggerJSONFormatter(b *testing.B) { + doLoggerBenchmarkWithFormatter(b, &JSONFormatter{}) +} + +func BenchmarkLoggerTextFormatter(b *testing.B) { + doLoggerBenchmarkWithFormatter(b, &TextFormatter{}) +} + +func doLoggerBenchmarkWithFormatter(b *testing.B, f Formatter) { + b.SetParallelism(100) + log := New() + log.Formatter = f + log.Out = ioutil.Discard + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + log. + WithField("foo1", "bar1"). + WithField("foo2", "bar2"). + Info("this is a dummy log") + } + }) +} From 4ed761c7f6384ada89c60f2f3b500844b49c4484 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Tue, 31 Jul 2018 18:08:27 +0200 Subject: [PATCH 366/547] Ensure a new entry data fields are empty Fixes #795 --- hook_test.go | 43 +++++++++++++++++++++++++++++++++++++++++++ logger.go | 1 + 2 files changed, 44 insertions(+) diff --git a/hook_test.go b/hook_test.go index 4fea7514e..80b93b8c6 100644 --- a/hook_test.go +++ b/hook_test.go @@ -1,10 +1,13 @@ package logrus import ( + "bytes" + "encoding/json" "sync" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type TestHook struct { @@ -85,6 +88,46 @@ func TestCanFireMultipleHooks(t *testing.T) { }) } +type SingleLevelModifyHook struct { + ModifyHook +} + +func (h *SingleLevelModifyHook) Levels() []Level { + return []Level{InfoLevel} +} + +func TestHookEntryIsPristine(t *testing.T) { + l := New() + b := &bytes.Buffer{} + l.Formatter = &JSONFormatter{} + l.Out = b + l.AddHook(&SingleLevelModifyHook{}) + + l.Error("error message") + data := map[string]string{} + err := json.Unmarshal(b.Bytes(), &data) + require.NoError(t, err) + _, ok := data["wow"] + require.False(t, ok) + b.Reset() + + l.Info("error message") + data = map[string]string{} + err = json.Unmarshal(b.Bytes(), &data) + require.NoError(t, err) + _, ok = data["wow"] + require.True(t, ok) + b.Reset() + + l.Error("error message") + data = map[string]string{} + err = json.Unmarshal(b.Bytes(), &data) + require.NoError(t, err) + _, ok = data["wow"] + require.False(t, ok) + b.Reset() +} + type ErrorHook struct { Fired bool } diff --git a/logger.go b/logger.go index 342f7977d..7fa8d7d92 100644 --- a/logger.go +++ b/logger.go @@ -85,6 +85,7 @@ func (logger *Logger) newEntry() *Entry { } func (logger *Logger) releaseEntry(entry *Entry) { + entry.Data = map[string]interface{}{} logger.entryPool.Put(entry) } From 01105720bf7445a27291e99a8cfcd79209ad550e Mon Sep 17 00:00:00 2001 From: Alessio Caiazza Date: Fri, 13 Jul 2018 17:33:25 +0200 Subject: [PATCH 367/547] Add CLICOLOR support This implement CLICOLOR and CLICOLOR_FORCE check on terminal coloring as defined in https://bixense.com/clicolors/ --- text_formatter.go | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index 3e5504030..cdf318516 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -3,6 +3,7 @@ package logrus import ( "bytes" "fmt" + "os" "sort" "strings" "sync" @@ -35,6 +36,9 @@ type TextFormatter struct { // Force disabling colors. DisableColors bool + // Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/ + OverrideColors bool + // Disable timestamp logging. useful when output is redirected to logging // system that already adds timestamps. DisableTimestamp bool @@ -78,6 +82,22 @@ func (f *TextFormatter) init(entry *Entry) { } } +func (f *TextFormatter) isColored() bool { + isColored := f.ForceColors || f.isTerminal + + if f.OverrideColors { + if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok && force != "0" { + isColored = true + } + + if os.Getenv("CLICOLOR") == "0" { + isColored = false + } + } + + return isColored && !f.DisableColors +} + // Format renders a single log entry func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { prefixFieldClashes(entry.Data, f.FieldMap) @@ -100,13 +120,11 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { f.Do(func() { f.init(entry) }) - isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors - timestampFormat := f.TimestampFormat if timestampFormat == "" { timestampFormat = defaultTimestampFormat } - if isColored { + if f.isColored() { f.printColored(b, entry, keys, timestampFormat) } else { if !f.DisableTimestamp { From 34626ba08efef68df9dfe00f8c179c2bf00c82d2 Mon Sep 17 00:00:00 2001 From: Kwok-kuen Cheung Date: Mon, 6 Aug 2018 00:43:49 +0800 Subject: [PATCH 368/547] Keep terminal check naming convention --- terminal_check_appengine.go | 2 +- text_formatter_js.go => terminal_check_js.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename text_formatter_js.go => terminal_check_js.go (51%) diff --git a/terminal_check_appengine.go b/terminal_check_appengine.go index 26a2867a7..2403de981 100644 --- a/terminal_check_appengine.go +++ b/terminal_check_appengine.go @@ -1,4 +1,4 @@ -// +build appengine js +// +build appengine package logrus diff --git a/text_formatter_js.go b/terminal_check_js.go similarity index 51% rename from text_formatter_js.go rename to terminal_check_js.go index d52803a69..0c209750a 100644 --- a/text_formatter_js.go +++ b/terminal_check_js.go @@ -6,6 +6,6 @@ import ( "io" ) -func (f *TextFormatter) checkIfTerminal(w io.Writer) bool { +func checkIfTerminal(w io.Writer) bool { return false } From 538b5bff9c72dbfe4fa81daeabe0f1f963b20b40 Mon Sep 17 00:00:00 2001 From: Kwok-kuen Cheung Date: Mon, 6 Aug 2018 00:43:58 +0800 Subject: [PATCH 369/547] Remove unnecessary text_formatter file --- text_formatter_other.go | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 text_formatter_other.go diff --git a/text_formatter_other.go b/text_formatter_other.go deleted file mode 100644 index 0d9704fc3..000000000 --- a/text_formatter_other.go +++ /dev/null @@ -1,3 +0,0 @@ -// +build !js - -package logrus From 9d1a5729476839ae377acb669d053301c1562e74 Mon Sep 17 00:00:00 2001 From: Dennis Date: Sun, 5 Aug 2018 22:40:58 +0200 Subject: [PATCH 370/547] Fixed missing brace after wrong merge --- text_formatter_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/text_formatter_test.go b/text_formatter_test.go index 72adda12a..092b19d3e 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -198,6 +198,7 @@ func TestNewlineBehavior(t *testing.T) { if !bytes.Contains(b, []byte("test message\n")) { t.Error("Double newline at end of Entry.Message did not result in a single newline after formatting") } +} func TestTextFormatterFieldMap(t *testing.T) { formatter := &TextFormatter{ From 93e0f3185e8edfedd9bf70c5d6ea830a97521d7a Mon Sep 17 00:00:00 2001 From: David Bariod Date: Thu, 9 Aug 2018 15:00:46 +0200 Subject: [PATCH 371/547] Fix for CLICOLOR_FORCE handling --- text_formatter.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index cdf318516..beb628f3a 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -37,7 +37,7 @@ type TextFormatter struct { DisableColors bool // Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/ - OverrideColors bool + EnvironmentOverrideColors bool // Disable timestamp logging. useful when output is redirected to logging // system that already adds timestamps. @@ -85,12 +85,12 @@ func (f *TextFormatter) init(entry *Entry) { func (f *TextFormatter) isColored() bool { isColored := f.ForceColors || f.isTerminal - if f.OverrideColors { + if f.EnvironmentOverrideColors { if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok && force != "0" { isColored = true - } - - if os.Getenv("CLICOLOR") == "0" { + } else if ok && force == "0" { + isColored = false + } else if os.Getenv("CLICOLOR") == "0" { isColored = false } } From 9fdb98374017d16823781a26df3426af2f9f2664 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Thu, 9 Aug 2018 15:01:49 +0200 Subject: [PATCH 372/547] Add unit test for TextFormatter.isColored --- text_formatter_test.go | 214 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) diff --git a/text_formatter_test.go b/text_formatter_test.go index 921d0522b..023f346cb 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "fmt" + "os" "strings" "testing" "time" @@ -216,5 +217,218 @@ func TestTextFormatterFieldMap(t *testing.T) { "Formatted output doesn't respect FieldMap") } +func TestTextFormatterIsColored(t *testing.T) { + params := []struct { + name string + expectedResult bool + isTerminal bool + disableColor bool + forceColor bool + envColor bool + clicolorIsSet bool + clicolorForceIsSet bool + clicolorVal string + clicolorForceVal string + }{ + // Default values + { + name: "testcase1", + expectedResult: false, + isTerminal: false, + disableColor: false, + forceColor: false, + envColor: false, + clicolorIsSet: false, + clicolorForceIsSet: false, + }, + // Output on terminal + { + name: "testcase2", + expectedResult: true, + isTerminal: true, + disableColor: false, + forceColor: false, + envColor: false, + clicolorIsSet: false, + clicolorForceIsSet: false, + }, + // Output on terminal with color disabled + { + name: "testcase3", + expectedResult: false, + isTerminal: true, + disableColor: true, + forceColor: false, + envColor: false, + clicolorIsSet: false, + clicolorForceIsSet: false, + }, + // Output not on terminal with color disabled + { + name: "testcase4", + expectedResult: false, + isTerminal: false, + disableColor: true, + forceColor: false, + envColor: false, + clicolorIsSet: false, + clicolorForceIsSet: false, + }, + // Output not on terminal with color forced + { + name: "testcase5", + expectedResult: true, + isTerminal: false, + disableColor: false, + forceColor: true, + envColor: false, + clicolorIsSet: false, + clicolorForceIsSet: false, + }, + // Output on terminal with clicolor set to "0" + { + name: "testcase6", + expectedResult: false, + isTerminal: true, + disableColor: false, + forceColor: false, + envColor: true, + clicolorIsSet: true, + clicolorForceIsSet: false, + clicolorVal: "0", + }, + // Output on terminal with clicolor set to "1" + { + name: "testcase7", + expectedResult: true, + isTerminal: true, + disableColor: false, + forceColor: false, + envColor: true, + clicolorIsSet: true, + clicolorForceIsSet: false, + clicolorVal: "1", + }, + // Output not on terminal with clicolor set to "0" + { + name: "testcase8", + expectedResult: false, + isTerminal: false, + disableColor: false, + forceColor: false, + envColor: true, + clicolorIsSet: true, + clicolorForceIsSet: false, + clicolorVal: "0", + }, + // Output not on terminal with clicolor set to "1" + { + name: "testcase9", + expectedResult: false, + isTerminal: false, + disableColor: false, + forceColor: false, + envColor: true, + clicolorIsSet: true, + clicolorForceIsSet: false, + clicolorVal: "1", + }, + // Output not on terminal with clicolor set to "1" and force color + { + name: "testcase10", + expectedResult: true, + isTerminal: false, + disableColor: false, + forceColor: true, + envColor: true, + clicolorIsSet: true, + clicolorForceIsSet: false, + clicolorVal: "1", + }, + // Output not on terminal with clicolor set to "0" and force color + { + name: "testcase11", + expectedResult: false, + isTerminal: false, + disableColor: false, + forceColor: true, + envColor: true, + clicolorIsSet: true, + clicolorForceIsSet: false, + clicolorVal: "0", + }, + // Output not on terminal with clicolor set to "1" + { + name: "testcase12", + expectedResult: false, + isTerminal: false, + disableColor: false, + forceColor: false, + envColor: true, + clicolorIsSet: true, + clicolorForceIsSet: false, + clicolorVal: "1", + }, + // Output not on terminal with clicolor_force set to "1" + { + name: "testcase13", + expectedResult: true, + isTerminal: false, + disableColor: false, + forceColor: false, + envColor: true, + clicolorIsSet: false, + clicolorForceIsSet: true, + clicolorForceVal: "1", + }, + // Output not on terminal with clicolor_force set to "0" + { + name: "testcase14", + expectedResult: false, + isTerminal: false, + disableColor: false, + forceColor: false, + envColor: true, + clicolorIsSet: false, + clicolorForceIsSet: true, + clicolorForceVal: "0", + }, + // Output on terminal with clicolor_force set to "0" + { + name: "testcase14", + expectedResult: false, + isTerminal: true, + disableColor: false, + forceColor: false, + envColor: true, + clicolorIsSet: false, + clicolorForceIsSet: true, + clicolorForceVal: "0", + }, + } + + defer os.Clearenv() + + for _, val := range params { + t.Run("textformatter_"+val.name, func(subT *testing.T) { + tf := TextFormatter{ + isTerminal: val.isTerminal, + DisableColors: val.disableColor, + ForceColors: val.forceColor, + EnvironmentOverrideColors: val.envColor, + } + os.Clearenv() + if val.clicolorIsSet { + os.Setenv("CLICOLOR", val.clicolorVal) + } + if val.clicolorForceIsSet { + os.Setenv("CLICOLOR_FORCE", val.clicolorForceVal) + } + res := tf.isColored() + assert.Equal(subT, val.expectedResult, res) + }) + } +} + // TODO add tests for sorting etc., this requires a parser for the text // formatter output. From f31c5d6dcc88db10acf512a7213c751203dcda5f Mon Sep 17 00:00:00 2001 From: David Bariod Date: Mon, 13 Aug 2018 17:27:32 +0200 Subject: [PATCH 373/547] Cleanup on unit test on isColored --- text_formatter_test.go | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/text_formatter_test.go b/text_formatter_test.go index 023f346cb..652102d3a 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -357,21 +357,9 @@ func TestTextFormatterIsColored(t *testing.T) { clicolorForceIsSet: false, clicolorVal: "0", }, - // Output not on terminal with clicolor set to "1" - { - name: "testcase12", - expectedResult: false, - isTerminal: false, - disableColor: false, - forceColor: false, - envColor: true, - clicolorIsSet: true, - clicolorForceIsSet: false, - clicolorVal: "1", - }, // Output not on terminal with clicolor_force set to "1" { - name: "testcase13", + name: "testcase12", expectedResult: true, isTerminal: false, disableColor: false, @@ -383,7 +371,7 @@ func TestTextFormatterIsColored(t *testing.T) { }, // Output not on terminal with clicolor_force set to "0" { - name: "testcase14", + name: "testcase13", expectedResult: false, isTerminal: false, disableColor: false, @@ -407,7 +395,12 @@ func TestTextFormatterIsColored(t *testing.T) { }, } - defer os.Clearenv() + cleanenv := func() { + os.Unsetenv("CLICOLOR") + os.Unsetenv("CLICOLOR_FORCE") + } + + defer cleanenv() for _, val := range params { t.Run("textformatter_"+val.name, func(subT *testing.T) { @@ -417,7 +410,7 @@ func TestTextFormatterIsColored(t *testing.T) { ForceColors: val.forceColor, EnvironmentOverrideColors: val.envColor, } - os.Clearenv() + cleanenv() if val.clicolorIsSet { os.Setenv("CLICOLOR", val.clicolorVal) } From c12668dd16f38e19a12909f74da10f44fbb4ed2d Mon Sep 17 00:00:00 2001 From: betrok Date: Wed, 22 Aug 2018 12:10:05 +0300 Subject: [PATCH 374/547] logger.ReplaceHooks --- logger.go | 6 ++++++ logrus_test.go | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/logger.go b/logger.go index 7fa8d7d92..53bbf95fd 100644 --- a/logger.go +++ b/logger.go @@ -336,3 +336,9 @@ func (logger *Logger) AddHook(hook Hook) { defer logger.mu.Unlock() logger.Hooks.Add(hook) } + +func (logger *Logger) ReplaceHooks(hooks LevelHooks) { + logger.mu.Lock() + logger.Hooks = hooks + logger.mu.Unlock() +} diff --git a/logrus_test.go b/logrus_test.go index 57fb8d1c6..7a9668697 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -421,6 +421,22 @@ func TestLoggingRaceWithHooksOnEntry(t *testing.T) { wg.Wait() } +func TestHooksReplace(t *testing.T) { + old, cur := &TestHook{}, &TestHook{} + + logger := New() + logger.AddHook(old) + + hooks := make(LevelHooks) + hooks.Add(cur) + logger.ReplaceHooks(hooks) + + logger.Info("test") + + assert.Equal(t, old.Fired, false) + assert.Equal(t, cur.Fired, true) +} + // Compile test func TestLogrusInterface(t *testing.T) { var buffer bytes.Buffer From 32d2d631fa56323b0936ae034115715140a2e1b8 Mon Sep 17 00:00:00 2001 From: betrok Date: Sun, 26 Aug 2018 14:40:51 +0300 Subject: [PATCH 375/547] return old hooks from RelplaceHooks --- logger.go | 5 ++++- logrus_test.go | 10 ++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/logger.go b/logger.go index 53bbf95fd..885f15056 100644 --- a/logger.go +++ b/logger.go @@ -337,8 +337,11 @@ func (logger *Logger) AddHook(hook Hook) { logger.Hooks.Add(hook) } -func (logger *Logger) ReplaceHooks(hooks LevelHooks) { +// ReplaceHooks replaces the logger hooks and returns the old ones +func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks { logger.mu.Lock() + oldHooks := logger.Hooks logger.Hooks = hooks logger.mu.Unlock() + return oldHooks } diff --git a/logrus_test.go b/logrus_test.go index 7a9668697..f6db6e979 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -3,6 +3,7 @@ package logrus import ( "bytes" "encoding/json" + "io/ioutil" "strconv" "strings" "sync" @@ -421,20 +422,25 @@ func TestLoggingRaceWithHooksOnEntry(t *testing.T) { wg.Wait() } -func TestHooksReplace(t *testing.T) { +func TestReplaceHooks(t *testing.T) { old, cur := &TestHook{}, &TestHook{} logger := New() + logger.SetOutput(ioutil.Discard) logger.AddHook(old) hooks := make(LevelHooks) hooks.Add(cur) - logger.ReplaceHooks(hooks) + replaced := logger.ReplaceHooks(hooks) logger.Info("test") assert.Equal(t, old.Fired, false) assert.Equal(t, cur.Fired, true) + + logger.ReplaceHooks(replaced) + logger.Info("test") + assert.Equal(t, old.Fired, true) } // Compile test From 5f7bb9569a1ed7a734dd082d8a576588767780ea Mon Sep 17 00:00:00 2001 From: Logan HAUSPIE Date: Sun, 26 Aug 2018 23:51:09 +0200 Subject: [PATCH 376/547] feat(LogLevel): taking in account code review from David Bariod --- entry.go | 60 +++++++++++++---------------------------- exported.go | 25 +++--------------- logger.go | 65 +++++++++++++++++---------------------------- logrus.go | 12 ++++----- logrus_test.go | 72 +++++++++++++++++++++++++------------------------- 5 files changed, 87 insertions(+), 147 deletions(-) diff --git a/entry.go b/entry.go index 82ef93c0f..8c6a389ee 100644 --- a/entry.go +++ b/entry.go @@ -151,7 +151,7 @@ func (entry *Entry) write() { } func (entry *Entry) Debug(args ...interface{}) { - if entry.IsDebugEnabled() { + if entry.Logger.IsLevelEnabled(DebugLevel) { entry.log(DebugLevel, fmt.Sprint(args...)) } } @@ -161,13 +161,13 @@ func (entry *Entry) Print(args ...interface{}) { } func (entry *Entry) Info(args ...interface{}) { - if entry.IsInfoEnabled() { + if entry.Logger.IsLevelEnabled(InfoLevel) { entry.log(InfoLevel, fmt.Sprint(args...)) } } func (entry *Entry) Warn(args ...interface{}) { - if entry.IsWarnEnabled() { + if entry.Logger.IsLevelEnabled(WarnLevel) { entry.log(WarnLevel, fmt.Sprint(args...)) } } @@ -177,20 +177,20 @@ func (entry *Entry) Warning(args ...interface{}) { } func (entry *Entry) Error(args ...interface{}) { - if entry.IsErrorEnabled() { + if entry.Logger.IsLevelEnabled(ErrorLevel) { entry.log(ErrorLevel, fmt.Sprint(args...)) } } func (entry *Entry) Fatal(args ...interface{}) { - if entry.IsFatalEnabled() { + if entry.Logger.IsLevelEnabled(FatalLevel) { entry.log(FatalLevel, fmt.Sprint(args...)) } Exit(1) } func (entry *Entry) Panic(args ...interface{}) { - if entry.IsPanicEnabled() { + if entry.Logger.IsLevelEnabled(PanicLevel) { entry.log(PanicLevel, fmt.Sprint(args...)) } panic(fmt.Sprint(args...)) @@ -199,13 +199,13 @@ func (entry *Entry) Panic(args ...interface{}) { // Entry Printf family functions func (entry *Entry) Debugf(format string, args ...interface{}) { - if entry.IsDebugEnabled() { + if entry.Logger.IsLevelEnabled(DebugLevel) { entry.Debug(fmt.Sprintf(format, args...)) } } func (entry *Entry) Infof(format string, args ...interface{}) { - if entry.IsInfoEnabled() { + if entry.Logger.IsLevelEnabled(InfoLevel) { entry.Info(fmt.Sprintf(format, args...)) } } @@ -215,7 +215,7 @@ func (entry *Entry) Printf(format string, args ...interface{}) { } func (entry *Entry) Warnf(format string, args ...interface{}) { - if entry.IsWarnEnabled() { + if entry.Logger.IsLevelEnabled(WarnLevel) { entry.Warn(fmt.Sprintf(format, args...)) } } @@ -225,20 +225,20 @@ func (entry *Entry) Warningf(format string, args ...interface{}) { } func (entry *Entry) Errorf(format string, args ...interface{}) { - if entry.IsErrorEnabled() { + if entry.Logger.IsLevelEnabled(ErrorLevel) { entry.Error(fmt.Sprintf(format, args...)) } } func (entry *Entry) Fatalf(format string, args ...interface{}) { - if entry.IsFatalEnabled() { + if entry.Logger.IsLevelEnabled(FatalLevel) { entry.Fatal(fmt.Sprintf(format, args...)) } Exit(1) } func (entry *Entry) Panicf(format string, args ...interface{}) { - if entry.IsPanicEnabled() { + if entry.Logger.IsLevelEnabled(PanicLevel) { entry.Panic(fmt.Sprintf(format, args...)) } } @@ -246,13 +246,13 @@ func (entry *Entry) Panicf(format string, args ...interface{}) { // Entry Println family functions func (entry *Entry) Debugln(args ...interface{}) { - if entry.IsDebugEnabled() { + if entry.Logger.IsLevelEnabled(DebugLevel) { entry.Debug(entry.sprintlnn(args...)) } } func (entry *Entry) Infoln(args ...interface{}) { - if entry.IsInfoEnabled() { + if entry.Logger.IsLevelEnabled(InfoLevel) { entry.Info(entry.sprintlnn(args...)) } } @@ -262,7 +262,7 @@ func (entry *Entry) Println(args ...interface{}) { } func (entry *Entry) Warnln(args ...interface{}) { - if entry.IsWarnEnabled() { + if entry.Logger.IsLevelEnabled(WarnLevel) { entry.Warn(entry.sprintlnn(args...)) } } @@ -272,48 +272,24 @@ func (entry *Entry) Warningln(args ...interface{}) { } func (entry *Entry) Errorln(args ...interface{}) { - if entry.IsErrorEnabled() { + if entry.Logger.IsLevelEnabled(ErrorLevel) { entry.Error(entry.sprintlnn(args...)) } } func (entry *Entry) Fatalln(args ...interface{}) { - if entry.IsFatalEnabled() { + if entry.Logger.IsLevelEnabled(FatalLevel) { entry.Fatal(entry.sprintlnn(args...)) } Exit(1) } func (entry *Entry) Panicln(args ...interface{}) { - if entry.IsPanicEnabled() { + if entry.Logger.IsLevelEnabled(PanicLevel) { entry.Panic(entry.sprintlnn(args...)) } } -func (entry *Entry) IsDebugEnabled() bool { - return entry.Logger.IsDebugEnabled() -} - -func (entry *Entry) IsInfoEnabled() bool { - return entry.Logger.IsInfoEnabled() -} - -func (entry *Entry) IsWarnEnabled() bool { - return entry.Logger.IsWarnEnabled() -} - -func (entry *Entry) IsErrorEnabled() bool { - return entry.Logger.IsErrorEnabled() -} - -func (entry *Entry) IsFatalEnabled() bool { - return entry.Logger.IsFatalEnabled() -} - -func (entry *Entry) IsPanicEnabled() bool { - return entry.Logger.IsPanicEnabled() -} - // Sprintlnn => Sprint no newline. This is to get the behavior of how // fmt.Sprintln where spaces are always added between operands, regardless of // their type. Instead of vendoring the Sprintln implementation to spare a diff --git a/exported.go b/exported.go index 764367106..fb2a7a1f0 100644 --- a/exported.go +++ b/exported.go @@ -34,28 +34,9 @@ func GetLevel() Level { return std.GetLevel() } -func IsDebugEnabled() bool { - return std.IsDebugEnabled() -} - -func IsInfoEnabled() bool { - return std.IsInfoEnabled() -} - -func IsWarnEnabled() bool { - return std.IsWarnEnabled() -} - -func IsErrorEnabled() bool { - return std.IsErrorEnabled() -} - -func IsFatalEnabled() bool { - return std.IsFatalEnabled() -} - -func IsPanicEnabled() bool { - return std.IsPanicEnabled() +// IsLevelEnabled checks if the log level of the standard logger is greater than the level param +func IsLevelEnabled(level Level) bool { + return std.IsLevelEnabled(level) } // AddHook adds a hook to the standard logger hooks. diff --git a/logger.go b/logger.go index 61bc1cc96..b67bfcbd3 100644 --- a/logger.go +++ b/logger.go @@ -122,7 +122,7 @@ func (logger *Logger) WithTime(t time.Time) *Entry { } func (logger *Logger) Debugf(format string, args ...interface{}) { - if logger.IsDebugEnabled() { + if logger.IsLevelEnabled(DebugLevel) { entry := logger.newEntry() entry.Debugf(format, args...) logger.releaseEntry(entry) @@ -130,7 +130,7 @@ func (logger *Logger) Debugf(format string, args ...interface{}) { } func (logger *Logger) Infof(format string, args ...interface{}) { - if logger.IsInfoEnabled() { + if logger.IsLevelEnabled(InfoLevel) { entry := logger.newEntry() entry.Infof(format, args...) logger.releaseEntry(entry) @@ -144,7 +144,7 @@ func (logger *Logger) Printf(format string, args ...interface{}) { } func (logger *Logger) Warnf(format string, args ...interface{}) { - if logger.IsWarnEnabled() { + if logger.IsLevelEnabled(WarnLevel) { entry := logger.newEntry() entry.Warnf(format, args...) logger.releaseEntry(entry) @@ -152,7 +152,7 @@ func (logger *Logger) Warnf(format string, args ...interface{}) { } func (logger *Logger) Warningf(format string, args ...interface{}) { - if logger.IsWarnEnabled() { + if logger.IsLevelEnabled(WarnLevel) { entry := logger.newEntry() entry.Warnf(format, args...) logger.releaseEntry(entry) @@ -160,7 +160,7 @@ func (logger *Logger) Warningf(format string, args ...interface{}) { } func (logger *Logger) Errorf(format string, args ...interface{}) { - if logger.IsErrorEnabled() { + if logger.IsLevelEnabled(ErrorLevel) { entry := logger.newEntry() entry.Errorf(format, args...) logger.releaseEntry(entry) @@ -168,7 +168,7 @@ func (logger *Logger) Errorf(format string, args ...interface{}) { } func (logger *Logger) Fatalf(format string, args ...interface{}) { - if logger.IsFatalEnabled() { + if logger.IsLevelEnabled(FatalLevel) { entry := logger.newEntry() entry.Fatalf(format, args...) logger.releaseEntry(entry) @@ -177,7 +177,7 @@ func (logger *Logger) Fatalf(format string, args ...interface{}) { } func (logger *Logger) Panicf(format string, args ...interface{}) { - if logger.IsPanicEnabled() { + if logger.IsLevelEnabled(PanicLevel) { entry := logger.newEntry() entry.Panicf(format, args...) logger.releaseEntry(entry) @@ -185,7 +185,7 @@ func (logger *Logger) Panicf(format string, args ...interface{}) { } func (logger *Logger) Debug(args ...interface{}) { - if logger.IsDebugEnabled() { + if logger.IsLevelEnabled(DebugLevel) { entry := logger.newEntry() entry.Debug(args...) logger.releaseEntry(entry) @@ -193,7 +193,7 @@ func (logger *Logger) Debug(args ...interface{}) { } func (logger *Logger) Info(args ...interface{}) { - if logger.IsInfoEnabled() { + if logger.IsLevelEnabled(InfoLevel) { entry := logger.newEntry() entry.Info(args...) logger.releaseEntry(entry) @@ -207,7 +207,7 @@ func (logger *Logger) Print(args ...interface{}) { } func (logger *Logger) Warn(args ...interface{}) { - if logger.IsWarnEnabled() { + if logger.IsLevelEnabled(WarnLevel) { entry := logger.newEntry() entry.Warn(args...) logger.releaseEntry(entry) @@ -215,7 +215,7 @@ func (logger *Logger) Warn(args ...interface{}) { } func (logger *Logger) Warning(args ...interface{}) { - if logger.IsWarnEnabled() { + if logger.IsLevelEnabled(WarnLevel) { entry := logger.newEntry() entry.Warn(args...) logger.releaseEntry(entry) @@ -223,7 +223,7 @@ func (logger *Logger) Warning(args ...interface{}) { } func (logger *Logger) Error(args ...interface{}) { - if logger.IsErrorEnabled() { + if logger.IsLevelEnabled(ErrorLevel) { entry := logger.newEntry() entry.Error(args...) logger.releaseEntry(entry) @@ -231,7 +231,7 @@ func (logger *Logger) Error(args ...interface{}) { } func (logger *Logger) Fatal(args ...interface{}) { - if logger.IsFatalEnabled() { + if logger.IsLevelEnabled(FatalLevel) { entry := logger.newEntry() entry.Fatal(args...) logger.releaseEntry(entry) @@ -240,7 +240,7 @@ func (logger *Logger) Fatal(args ...interface{}) { } func (logger *Logger) Panic(args ...interface{}) { - if logger.IsPanicEnabled() { + if logger.IsLevelEnabled(PanicLevel) { entry := logger.newEntry() entry.Panic(args...) logger.releaseEntry(entry) @@ -248,7 +248,7 @@ func (logger *Logger) Panic(args ...interface{}) { } func (logger *Logger) Debugln(args ...interface{}) { - if logger.IsDebugEnabled() { + if logger.IsLevelEnabled(DebugLevel) { entry := logger.newEntry() entry.Debugln(args...) logger.releaseEntry(entry) @@ -256,7 +256,7 @@ func (logger *Logger) Debugln(args ...interface{}) { } func (logger *Logger) Infoln(args ...interface{}) { - if logger.IsInfoEnabled() { + if logger.IsLevelEnabled(InfoLevel) { entry := logger.newEntry() entry.Infoln(args...) logger.releaseEntry(entry) @@ -270,7 +270,7 @@ func (logger *Logger) Println(args ...interface{}) { } func (logger *Logger) Warnln(args ...interface{}) { - if logger.IsWarnEnabled() { + if logger.IsLevelEnabled(WarnLevel) { entry := logger.newEntry() entry.Warnln(args...) logger.releaseEntry(entry) @@ -278,7 +278,7 @@ func (logger *Logger) Warnln(args ...interface{}) { } func (logger *Logger) Warningln(args ...interface{}) { - if logger.IsWarnEnabled() { + if logger.IsLevelEnabled(WarnLevel) { entry := logger.newEntry() entry.Warnln(args...) logger.releaseEntry(entry) @@ -286,7 +286,7 @@ func (logger *Logger) Warningln(args ...interface{}) { } func (logger *Logger) Errorln(args ...interface{}) { - if logger.IsErrorEnabled() { + if logger.IsLevelEnabled(ErrorLevel) { entry := logger.newEntry() entry.Errorln(args...) logger.releaseEntry(entry) @@ -294,7 +294,7 @@ func (logger *Logger) Errorln(args ...interface{}) { } func (logger *Logger) Fatalln(args ...interface{}) { - if logger.IsFatalEnabled() { + if logger.IsLevelEnabled(FatalLevel) { entry := logger.newEntry() entry.Fatalln(args...) logger.releaseEntry(entry) @@ -303,7 +303,7 @@ func (logger *Logger) Fatalln(args ...interface{}) { } func (logger *Logger) Panicln(args ...interface{}) { - if logger.IsPanicEnabled() { + if logger.IsLevelEnabled(PanicLevel) { entry := logger.newEntry() entry.Panicln(args...) logger.releaseEntry(entry) @@ -338,28 +338,11 @@ func (logger *Logger) AddHook(hook Hook) { logger.Hooks.Add(hook) } -func (logger *Logger) IsDebugEnabled() bool { - return logger.level() >= DebugLevel +// IsLevelEnabled checks if the log level of the logger is greater than the level param +func (logger *Logger) IsLevelEnabled(level Level) bool { + return logger.level() >= level } -func (logger *Logger) IsInfoEnabled() bool { - return logger.level() >= InfoLevel -} - -func (logger *Logger) IsWarnEnabled() bool { - return logger.level() >= WarnLevel -} - -func (logger *Logger) IsErrorEnabled() bool { - return logger.level() >= ErrorLevel -} - -func (logger *Logger) IsFatalEnabled() bool { - return logger.level() >= FatalLevel -} - -func (logger *Logger) IsPanicEnabled() bool { - return logger.level() >= PanicLevel // SetFormatter sets the logger formatter. func (logger *Logger) SetFormatter(formatter Formatter) { logger.mu.Lock() diff --git a/logrus.go b/logrus.go index 33e07fa0e..fa0b9dea8 100644 --- a/logrus.go +++ b/logrus.go @@ -141,10 +141,10 @@ type FieldLogger interface { Fatalln(args ...interface{}) Panicln(args ...interface{}) - IsDebugEnabled() bool - IsInfoEnabled() bool - IsWarnEnabled() bool - IsErrorEnabled() bool - IsFatalEnabled() bool - IsPanicEnabled() bool + // IsDebugEnabled() bool + // IsInfoEnabled() bool + // IsWarnEnabled() bool + // IsErrorEnabled() bool + // IsFatalEnabled() bool + // IsPanicEnabled() bool } diff --git a/logrus_test.go b/logrus_test.go index 4820e332f..97d15d7fd 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -488,50 +488,50 @@ func TestEntryWriter(t *testing.T) { func TestLogLevelEnabled(t *testing.T) { log := New() log.SetLevel(PanicLevel) - assert.Equal(t, true, log.IsPanicEnabled()) - assert.Equal(t, false, log.IsFatalEnabled()) - assert.Equal(t, false, log.IsErrorEnabled()) - assert.Equal(t, false, log.IsWarnEnabled()) - assert.Equal(t, false, log.IsInfoEnabled()) - assert.Equal(t, false, log.IsDebugEnabled()) + assert.Equal(t, true, log.IsLevelEnabled(PanicLevel)) + assert.Equal(t, false, log.IsLevelEnabled(FatalLevel)) + assert.Equal(t, false, log.IsLevelEnabled(ErrorLevel)) + assert.Equal(t, false, log.IsLevelEnabled(WarnLevel)) + assert.Equal(t, false, log.IsLevelEnabled(InfoLevel)) + assert.Equal(t, false, log.IsLevelEnabled(DebugLevel)) log.SetLevel(FatalLevel) - assert.Equal(t, true, log.IsPanicEnabled()) - assert.Equal(t, true, log.IsFatalEnabled()) - assert.Equal(t, false, log.IsErrorEnabled()) - assert.Equal(t, false, log.IsWarnEnabled()) - assert.Equal(t, false, log.IsInfoEnabled()) - assert.Equal(t, false, log.IsDebugEnabled()) + assert.Equal(t, true, log.IsLevelEnabled(PanicLevel)) + assert.Equal(t, true, log.IsLevelEnabled(FatalLevel)) + assert.Equal(t, false, log.IsLevelEnabled(ErrorLevel)) + assert.Equal(t, false, log.IsLevelEnabled(WarnLevel)) + assert.Equal(t, false, log.IsLevelEnabled(InfoLevel)) + assert.Equal(t, false, log.IsLevelEnabled(DebugLevel)) log.SetLevel(ErrorLevel) - assert.Equal(t, true, log.IsPanicEnabled()) - assert.Equal(t, true, log.IsFatalEnabled()) - assert.Equal(t, true, log.IsErrorEnabled()) - assert.Equal(t, false, log.IsWarnEnabled()) - assert.Equal(t, false, log.IsInfoEnabled()) - assert.Equal(t, false, log.IsDebugEnabled()) + assert.Equal(t, true, log.IsLevelEnabled(PanicLevel)) + assert.Equal(t, true, log.IsLevelEnabled(FatalLevel)) + assert.Equal(t, true, log.IsLevelEnabled(ErrorLevel)) + assert.Equal(t, false, log.IsLevelEnabled(WarnLevel)) + assert.Equal(t, false, log.IsLevelEnabled(InfoLevel)) + assert.Equal(t, false, log.IsLevelEnabled(DebugLevel)) log.SetLevel(WarnLevel) - assert.Equal(t, true, log.IsPanicEnabled()) - assert.Equal(t, true, log.IsFatalEnabled()) - assert.Equal(t, true, log.IsErrorEnabled()) - assert.Equal(t, true, log.IsWarnEnabled()) - assert.Equal(t, false, log.IsInfoEnabled()) - assert.Equal(t, false, log.IsDebugEnabled()) + assert.Equal(t, true, log.IsLevelEnabled(PanicLevel)) + assert.Equal(t, true, log.IsLevelEnabled(FatalLevel)) + assert.Equal(t, true, log.IsLevelEnabled(ErrorLevel)) + assert.Equal(t, true, log.IsLevelEnabled(WarnLevel)) + assert.Equal(t, false, log.IsLevelEnabled(InfoLevel)) + assert.Equal(t, false, log.IsLevelEnabled(DebugLevel)) log.SetLevel(InfoLevel) - assert.Equal(t, true, log.IsPanicEnabled()) - assert.Equal(t, true, log.IsFatalEnabled()) - assert.Equal(t, true, log.IsErrorEnabled()) - assert.Equal(t, true, log.IsWarnEnabled()) - assert.Equal(t, true, log.IsInfoEnabled()) - assert.Equal(t, false, log.IsDebugEnabled()) + assert.Equal(t, true, log.IsLevelEnabled(PanicLevel)) + assert.Equal(t, true, log.IsLevelEnabled(FatalLevel)) + assert.Equal(t, true, log.IsLevelEnabled(ErrorLevel)) + assert.Equal(t, true, log.IsLevelEnabled(WarnLevel)) + assert.Equal(t, true, log.IsLevelEnabled(InfoLevel)) + assert.Equal(t, false, log.IsLevelEnabled(DebugLevel)) log.SetLevel(DebugLevel) - assert.Equal(t, true, log.IsPanicEnabled()) - assert.Equal(t, true, log.IsFatalEnabled()) - assert.Equal(t, true, log.IsErrorEnabled()) - assert.Equal(t, true, log.IsWarnEnabled()) - assert.Equal(t, true, log.IsInfoEnabled()) - assert.Equal(t, true, log.IsDebugEnabled()) + assert.Equal(t, true, log.IsLevelEnabled(PanicLevel)) + assert.Equal(t, true, log.IsLevelEnabled(FatalLevel)) + assert.Equal(t, true, log.IsLevelEnabled(ErrorLevel)) + assert.Equal(t, true, log.IsLevelEnabled(WarnLevel)) + assert.Equal(t, true, log.IsLevelEnabled(InfoLevel)) + assert.Equal(t, true, log.IsLevelEnabled(DebugLevel)) } From 210badcbc3370d97c5628bb03aa12379e29a6d71 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Thu, 30 Aug 2018 07:17:54 +0200 Subject: [PATCH 377/547] Add previously forgotten v1.0.6 description in changelog fixes #802 --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bd1deb29..ff40b2ab1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +# 1.0.6 + +This new release introduces: + * a new api WithTime which allows to easily force the time of the log entry + which is mostly useful for logger wrapper + * a fix reverting the immutability of the entry given as parameter to the hooks + a new configuration field of the json formatter in order to put all the fields + in a nested dictionnary + * a new SetOutput method in the Logger + * a new configuration of the textformatter to configure the name of the default keys + * a new configuration of the text formatter to disable the level truncation + # 1.0.5 * Fix hooks race (#707) From 3bf8f006c9b8744b88bed4437835937cf5481acd Mon Sep 17 00:00:00 2001 From: David Bariod Date: Thu, 30 Aug 2018 22:06:53 +0200 Subject: [PATCH 378/547] bump go toolchain version in travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2f19b4a75..2bd349679 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: go go: - - 1.9.x - 1.10.x + - 1.11.x env: - GOMAXPROCS=4 GORACE=halt_on_error=1 install: From 36652817d2d3b9ac941234c90baab4f5fd6abe5e Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sun, 2 Sep 2018 20:22:06 +0200 Subject: [PATCH 379/547] Use syslog instead of airbrake as syslog example The purpose is to reduce package dependencies, fixes #809. --- example_hook_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/example_hook_test.go b/example_hook_test.go index d4ddffca3..b997c56e8 100644 --- a/example_hook_test.go +++ b/example_hook_test.go @@ -2,7 +2,8 @@ package logrus_test import ( "github.com/sirupsen/logrus" - "gopkg.in/gemnasium/logrus-airbrake-hook.v2" + slhooks "github.com/sirupsen/logrus/hooks/syslog" + "log/syslog" "os" ) @@ -10,7 +11,9 @@ 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")) + if sl, err := slhooks.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, ""); err != nil { + log.Hooks.Add(sl) + } log.Out = os.Stdout log.WithFields(logrus.Fields{ From 32bd75c5d2392496444c0e51c8d5e6c700f00b70 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Mon, 3 Sep 2018 21:58:50 +0200 Subject: [PATCH 380/547] Fix example build on windows --- example_hook_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/example_hook_test.go b/example_hook_test.go index b997c56e8..4e4ea1dc8 100644 --- a/example_hook_test.go +++ b/example_hook_test.go @@ -1,3 +1,5 @@ +// +build !windows + package logrus_test import ( From 22e4477c909319498157ac88eea22e00313edbde Mon Sep 17 00:00:00 2001 From: David Bariod Date: Tue, 4 Sep 2018 22:15:13 +0200 Subject: [PATCH 381/547] commit to trigger appveyor build --- example_hook_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/example_hook_test.go b/example_hook_test.go index 4e4ea1dc8..28bd8ea20 100644 --- a/example_hook_test.go +++ b/example_hook_test.go @@ -9,6 +9,7 @@ import ( "os" ) +// An example on how to use a hook func Example_hook() { var log = logrus.New() log.Formatter = new(logrus.TextFormatter) // default From e19b7e4b24592ac1140ccef49c03de588e9fcddc Mon Sep 17 00:00:00 2001 From: Dave Goddard Date: Thu, 6 Sep 2018 09:49:06 -0300 Subject: [PATCH 382/547] Add go module support Travis will only run the "go get" install lines if using go 1.10.x --- .travis.yml | 17 +++++++++-------- go.mod | 23 +++++++++++++++++++++++ go.sum | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 8 deletions(-) create mode 100644 go.mod create mode 100644 go.sum diff --git a/.travis.yml b/.travis.yml index 2bd349679..de38feb66 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,14 @@ language: go -go: - - 1.10.x - - 1.11.x 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 - - go get golang.org/x/sys/unix - - go get golang.org/x/sys/windows +matrix: + include: + - go: 1.10.x + 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 + - go: 1.11.x script: - go test -race -v ./... diff --git a/go.mod b/go.mod new file mode 100644 index 000000000..7946fe908 --- /dev/null +++ b/go.mod @@ -0,0 +1,23 @@ +module github.com/dgodd/logrus + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fsnotify/fsnotify v1.4.7 // indirect + github.com/golang/protobuf v1.2.0 // indirect + github.com/hpcloud/tail v1.0.0 // indirect + github.com/onsi/ginkgo v1.6.0 // indirect + github.com/onsi/gomega v1.4.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/sirupsen/logrus v1.0.6 + github.com/stretchr/testify v1.2.2 + golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 + golang.org/x/net v0.0.0-20180826012351-8a410e7b638d // indirect + golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f // indirect + golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 + golang.org/x/text v0.3.0 // indirect + gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect + gopkg.in/fsnotify.v1 v1.4.7 // indirect + gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect + gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect + gopkg.in/yaml.v2 v2.2.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 000000000..24aaefeb5 --- /dev/null +++ b/go.sum @@ -0,0 +1,40 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.0.6 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From c4b9a6525fd094d0a95a998683694be830f35819 Mon Sep 17 00:00:00 2001 From: William Huang Date: Thu, 6 Sep 2018 21:11:16 -0500 Subject: [PATCH 383/547] Fix spelling in Entry.Buffer comment --- entry.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entry.go b/entry.go index 8c6a389ee..4efedddfe 100644 --- a/entry.go +++ b/entry.go @@ -41,7 +41,7 @@ type Entry struct { // Message passed to Debug, Info, Warn, Error, Fatal or Panic Message string - // When formatter is called in entry.log(), an Buffer may be set to entry + // When formatter is called in entry.log(), a Buffer may be set to entry Buffer *bytes.Buffer } From f5a0486c1c872c8d524b12f69d43bc246bf23daa Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sat, 8 Sep 2018 08:27:05 +0200 Subject: [PATCH 384/547] Fix module name and remove unused dependencies --- .travis.yml | 2 ++ go.mod | 16 +--------------- go.sum | 30 ------------------------------ 3 files changed, 3 insertions(+), 45 deletions(-) diff --git a/.travis.yml b/.travis.yml index de38feb66..001842f11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,5 +10,7 @@ matrix: - go get golang.org/x/sys/unix - go get golang.org/x/sys/windows - go: 1.11.x + install: + - go mod download script: - go test -race -v ./... diff --git a/go.mod b/go.mod index 7946fe908..faee3923d 100644 --- a/go.mod +++ b/go.mod @@ -1,23 +1,9 @@ -module github.com/dgodd/logrus +module github.com/sirupsen/logrus require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fsnotify/fsnotify v1.4.7 // indirect - github.com/golang/protobuf v1.2.0 // indirect - github.com/hpcloud/tail v1.0.0 // indirect - github.com/onsi/ginkgo v1.6.0 // indirect - github.com/onsi/gomega v1.4.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/sirupsen/logrus v1.0.6 github.com/stretchr/testify v1.2.2 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 - golang.org/x/net v0.0.0-20180826012351-8a410e7b638d // indirect - golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f // indirect golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 - golang.org/x/text v0.3.0 // indirect - gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect - gopkg.in/fsnotify.v1 v1.4.7 // indirect - gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect - gopkg.in/yaml.v2 v2.2.1 // indirect ) diff --git a/go.sum b/go.sum index 24aaefeb5..7fd44a1a3 100644 --- a/go.sum +++ b/go.sum @@ -1,40 +1,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sirupsen/logrus v1.0.6 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From b7f9034d8ed89ff2446515ceaa67909406968027 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sat, 8 Sep 2018 09:44:05 +0200 Subject: [PATCH 385/547] Fix travis build for go 1.11 with modules --- .travis.yml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 001842f11..dd1218c18 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,11 +6,23 @@ matrix: - go: 1.10.x install: - go get github.com/stretchr/testify/assert - - go get gopkg.in/gemnasium/logrus-airbrake-hook.v2 + - go get golang.org/x/crypto/ssh/terminal - go get golang.org/x/sys/unix - go get golang.org/x/sys/windows + script: + - go test -race -v ./... - go: 1.11.x + env: GO111MODULE=on install: - go mod download -script: - - go test -race -v ./... + script: + - go test -race -v ./... + - go: 1.11.x + env: GO111MODULE=off + install: + - go get github.com/stretchr/testify/assert + - go get golang.org/x/crypto/ssh/terminal + - go get golang.org/x/sys/unix + - go get golang.org/x/sys/windows + script: + - go test -race -v ./... From f176ccf4d1fe1656fd97321d80754aab3f87f92b Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sun, 16 Sep 2018 10:14:59 +0200 Subject: [PATCH 386/547] Fix gopherjs build constraint name --- terminal_check_windows.go | 2 +- terminal_windows.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/terminal_check_windows.go b/terminal_check_windows.go index 17ebe8051..3b9d2864c 100644 --- a/terminal_check_windows.go +++ b/terminal_check_windows.go @@ -1,4 +1,4 @@ -// +build !appengine,!gopherjs,windows +// +build !appengine,!js,windows package logrus diff --git a/terminal_windows.go b/terminal_windows.go index 2494950fc..b4ef5286c 100644 --- a/terminal_windows.go +++ b/terminal_windows.go @@ -1,4 +1,4 @@ -// +build !appengine,!gopherjs,windows +// +build !appengine,!js,windows package logrus From c027b939b993c3abb59221a340ad8ad3b0e68c13 Mon Sep 17 00:00:00 2001 From: Mark Dittmer Date: Mon, 17 Sep 2018 13:50:48 -0400 Subject: [PATCH 387/547] Fix AppEngine builds `go build -tags appengine [...]` currently yields: ``` .../github.com/sirupsen/logrus/text_formatter.go:84:4: undefined: initTerminal ``` This change implements `initTerminal` for AppEngine builds. --- terminal_appengine.go | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 terminal_appengine.go diff --git a/terminal_appengine.go b/terminal_appengine.go new file mode 100644 index 000000000..2f344026d --- /dev/null +++ b/terminal_appengine.go @@ -0,0 +1,11 @@ +// Based on ssh/terminal: +// Copyright 2013 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 appengine + +package logrus + +func initTerminal(w io.Writer) { +} From d1d997e7ea3c3198a53b0a27f1d7bf55d83abcf2 Mon Sep 17 00:00:00 2001 From: Mark Dittmer Date: Mon, 17 Sep 2018 13:52:43 -0400 Subject: [PATCH 388/547] Fix copypasta --- terminal_appengine.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/terminal_appengine.go b/terminal_appengine.go index 2f344026d..72f679cdb 100644 --- a/terminal_appengine.go +++ b/terminal_appengine.go @@ -1,5 +1,5 @@ // Based on ssh/terminal: -// Copyright 2013 The Go Authors. All rights reserved. +// Copyright 2018 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. @@ -7,5 +7,7 @@ package logrus +import "io" + func initTerminal(w io.Writer) { } From 38b800eff5178e48d0ceec95968cd595bc74b0e3 Mon Sep 17 00:00:00 2001 From: Mark Dittmer Date: Mon, 17 Sep 2018 13:56:18 -0400 Subject: [PATCH 389/547] Add AppEngine test configurations to travis to a void regression --- .travis.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.travis.yml b/.travis.yml index dd1218c18..1f953bebd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,3 +26,26 @@ matrix: - go get golang.org/x/sys/windows script: - go test -race -v ./... + - go: 1.10.x + install: + - go get github.com/stretchr/testify/assert + - go get golang.org/x/crypto/ssh/terminal + - go get golang.org/x/sys/unix + - go get golang.org/x/sys/windows + script: + - go test -race -v -tags appengine ./... + - go: 1.11.x + env: GO111MODULE=on + install: + - go mod download + script: + - go test -race -v -tags appengine ./... + - go: 1.11.x + env: GO111MODULE=off + install: + - go get github.com/stretchr/testify/assert + - go get golang.org/x/crypto/ssh/terminal + - go get golang.org/x/sys/unix + - go get golang.org/x/sys/windows + script: + - go test -race -v -tags appengine ./... From cbe4471a6b9aab032fab9d30cd3af907dc08a6d9 Mon Sep 17 00:00:00 2001 From: Mark Dittmer Date: Tue, 18 Sep 2018 13:54:23 -0400 Subject: [PATCH 390/547] DisableColors in two tests to fix AppEngine configuration --- example_basic_test.go | 4 +++- example_hook_test.go | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/example_basic_test.go b/example_basic_test.go index a2acf550c..5f3849bff 100644 --- a/example_basic_test.go +++ b/example_basic_test.go @@ -1,14 +1,16 @@ package logrus_test import ( - "github.com/sirupsen/logrus" "os" + + "github.com/sirupsen/logrus" ) func Example_basic() { var log = logrus.New() log.Formatter = new(logrus.JSONFormatter) log.Formatter = new(logrus.TextFormatter) //default + log.Formatter.(*logrus.TextFormatter).DisableColors = true // remove colors log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true // remove timestamp from test output log.Level = logrus.DebugLevel log.Out = os.Stdout diff --git a/example_hook_test.go b/example_hook_test.go index 28bd8ea20..15118d26b 100644 --- a/example_hook_test.go +++ b/example_hook_test.go @@ -3,16 +3,18 @@ package logrus_test import ( - "github.com/sirupsen/logrus" - slhooks "github.com/sirupsen/logrus/hooks/syslog" "log/syslog" "os" + + "github.com/sirupsen/logrus" + slhooks "github.com/sirupsen/logrus/hooks/syslog" ) // An example on how to use a hook func Example_hook() { var log = logrus.New() log.Formatter = new(logrus.TextFormatter) // default + log.Formatter.(*logrus.TextFormatter).DisableColors = true // remove colors log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true // remove timestamp from test output if sl, err := slhooks.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, ""); err != nil { log.Hooks.Add(sl) From 7b4c7a2f946ef91f78bb70f9a928adc15de40924 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Wed, 19 Sep 2018 13:46:16 +0200 Subject: [PATCH 391/547] Add missing module dependency for windows build --- go.mod | 1 + go.sum | 2 ++ hooks/syslog/syslog_test.go | 2 ++ 3 files changed, 5 insertions(+) diff --git a/go.mod b/go.mod index faee3923d..f4fed02fb 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,7 @@ module github.com/sirupsen/logrus require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 diff --git a/go.sum b/go.sum index 7fd44a1a3..1f0d71964 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe h1:CHRGQ8V7OlCYtwaKPJi3iA7J+YdNKdo8j7nG5IgDhjs= +github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= diff --git a/hooks/syslog/syslog_test.go b/hooks/syslog/syslog_test.go index 5ec3a4445..bec6efd5a 100644 --- a/hooks/syslog/syslog_test.go +++ b/hooks/syslog/syslog_test.go @@ -1,3 +1,5 @@ +// +build !windows,!nacl,!plan9 + package syslog import ( From 9ac3a59e90b75257b1b992be40d68195f8203765 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Tue, 25 Sep 2018 13:45:23 +0200 Subject: [PATCH 392/547] Add custom sorting function in text formatter --- text_formatter.go | 52 ++++++++++++++++++++++++++++++++---------- text_formatter_test.go | 34 +++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 14 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index 6aee14ff3..67fb686c6 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -55,6 +55,9 @@ type TextFormatter struct { // be desired. DisableSorting bool + // The keys sorting function, when uninitialized it uses sort.Strings. + SortingFunc func([]string) + // Disables the truncation of the level text to 4 characters. DisableLevelTruncation bool @@ -73,7 +76,7 @@ type TextFormatter struct { // FieldKeyMsg: "@message"}} FieldMap FieldMap - sync.Once + terminalInitOnce sync.Once } func (f *TextFormatter) init(entry *Entry) { @@ -111,8 +114,29 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { keys = append(keys, k) } + fixedKeys := make([]string, 0, 3+len(entry.Data)) + if !f.DisableTimestamp { + fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime)) + } + fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLevel)) + if entry.Message != "" { + fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyMsg)) + } + if !f.DisableSorting { - sort.Strings(keys) + if f.SortingFunc == nil { + sort.Strings(keys) + fixedKeys = append(fixedKeys, keys...) + } else { + if !f.isColored() { + fixedKeys = append(fixedKeys, keys...) + f.SortingFunc(fixedKeys) + } else { + f.SortingFunc(keys) + } + } + } else { + fixedKeys = append(fixedKeys, keys...) } var b *bytes.Buffer @@ -122,7 +146,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { b = &bytes.Buffer{} } - f.Do(func() { f.init(entry) }) + f.terminalInitOnce.Do(func() { f.init(entry) }) timestampFormat := f.TimestampFormat if timestampFormat == "" { @@ -131,15 +155,19 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { if f.isColored() { f.printColored(b, entry, keys, timestampFormat) } else { - if !f.DisableTimestamp { - f.appendKeyValue(b, f.FieldMap.resolve(FieldKeyTime), entry.Time.Format(timestampFormat)) - } - f.appendKeyValue(b, f.FieldMap.resolve(FieldKeyLevel), entry.Level.String()) - if entry.Message != "" { - f.appendKeyValue(b, f.FieldMap.resolve(FieldKeyMsg), entry.Message) - } - for _, key := range keys { - f.appendKeyValue(b, key, entry.Data[key]) + for _, key := range fixedKeys { + var value interface{} + switch key { + case f.FieldMap.resolve(FieldKeyTime): + value = entry.Time.Format(timestampFormat) + case f.FieldMap.resolve(FieldKeyLevel): + value = entry.Level.String() + case f.FieldMap.resolve(FieldKeyMsg): + value = entry.Message + default: + value = entry.Data[key] + } + f.appendKeyValue(b, key, value) } } diff --git a/text_formatter_test.go b/text_formatter_test.go index b57ef7cee..b0d3a916a 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -5,11 +5,13 @@ import ( "errors" "fmt" "os" + "sort" "strings" "testing" "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestFormatting(t *testing.T) { @@ -446,5 +448,33 @@ func TestTextFormatterIsColored(t *testing.T) { } } -// TODO add tests for sorting etc., this requires a parser for the text -// formatter output. +func TestCustomSorting(t *testing.T) { + formatter := &TextFormatter{ + DisableColors: true, + SortingFunc: func(keys []string) { + sort.Slice(keys, func(i, j int) bool { + if keys[j] == "prefix" { + return false + } + if keys[i] == "prefix" { + return true + } + return strings.Compare(keys[i], keys[j]) == -1 + }) + }, + } + + entry := &Entry{ + Message: "Testing custom sort function", + Time: time.Now(), + Level: InfoLevel, + Data: Fields{ + "test": "testvalue", + "prefix": "the application prefix", + "blablabla": "blablabla", + }, + } + b, err := formatter.Format(entry) + require.NoError(t, err) + require.True(t, strings.HasPrefix(string(b), "prefix="), "format output is %q", string(b)) +} From 39b4df8e4d3138b2a2ef4f50875e02e1523a228f Mon Sep 17 00:00:00 2001 From: David Bariod Date: Tue, 25 Sep 2018 21:35:18 +0200 Subject: [PATCH 393/547] Update changelog for v1.1.0 release --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff40b2ab1..170269608 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +# 1.1.0 +This new release introduces: + * several fixes: + * a fix for a race condition on entry formatting + * proper cleanup of previously used entries before putting them back in the pool + * the extra new line at the end of message in text formatter has been removed + * a new global public API to check if a level is activated: IsLevelEnabled + * the following methods have been added to the Logger object + * IsLevelEnabled + * SetFormatter + * SetOutput + * ReplaceHooks + * introduction of go module + * an indent configuration for the json formatter + * output colour support for windows + * the field sort function is now configurable for text formatter + * the CLICOLOR and CLICOLOR\_FORCE environment variable support in text formater + # 1.0.6 This new release introduces: From c4d55a223001ecc48c31f2e2080a9daeb4ea40b8 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sun, 30 Sep 2018 22:51:02 +0200 Subject: [PATCH 394/547] Skip func type value in fields. We skip those unprintable fields and an error field instead of dropping the whole trace. Fixes #642 --- entry.go | 16 ++++++++++++++-- formatter.go | 15 ++++++++++++++- json_formatter.go | 10 +++------- logger_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ text_formatter.go | 7 ++++++- 5 files changed, 79 insertions(+), 11 deletions(-) create mode 100644 logger_test.go diff --git a/entry.go b/entry.go index 4efedddfe..ca634a609 100644 --- a/entry.go +++ b/entry.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "os" + "reflect" "sync" "time" ) @@ -43,6 +44,9 @@ type Entry struct { // When formatter is called in entry.log(), a Buffer may be set to entry Buffer *bytes.Buffer + + // err may contain a field formatting error + err string } func NewEntry(logger *Logger) *Entry { @@ -80,10 +84,18 @@ func (entry *Entry) WithFields(fields Fields) *Entry { for k, v := range entry.Data { data[k] = v } + var field_err string for k, v := range fields { - data[k] = v + if t := reflect.TypeOf(v); t != nil && t.Kind() == reflect.Func { + field_err = fmt.Sprintf("can not add field %q", k) + if entry.err != "" { + field_err = entry.err + ", " + field_err + } + } else { + data[k] = v + } } - return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time} + return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: field_err} } // Overrides the time of the Entry. diff --git a/formatter.go b/formatter.go index 83c74947b..be2f3fcee 100644 --- a/formatter.go +++ b/formatter.go @@ -2,7 +2,14 @@ package logrus import "time" -const defaultTimestampFormat = time.RFC3339 +// Default key names for the default fields +const ( + defaultTimestampFormat = time.RFC3339 + FieldKeyMsg = "msg" + FieldKeyLevel = "level" + FieldKeyTime = "time" + FieldKeyLogrusError = "logrus_error" +) // The Formatter interface is used to implement a custom Formatter. It takes an // `Entry`. It exposes all the fields, including the default ones: @@ -48,4 +55,10 @@ func prefixFieldClashes(data Fields, fieldMap FieldMap) { data["fields."+levelKey] = l delete(data, levelKey) } + + logrusErrKey := fieldMap.resolve(FieldKeyLogrusError) + if l, ok := data[logrusErrKey]; ok { + data["fields."+logrusErrKey] = l + delete(data, logrusErrKey) + } } diff --git a/json_formatter.go b/json_formatter.go index d3dadefe6..ef8d07460 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -11,13 +11,6 @@ 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" - FieldKeyTime = "time" -) - func (f FieldMap) resolve(key fieldKey) string { if k, ok := f[key]; ok { return k @@ -79,6 +72,9 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { timestampFormat = defaultTimestampFormat } + if entry.err != "" { + data[f.FieldMap.resolve(FieldKeyLogrusError)] = entry.err + } if !f.DisableTimestamp { data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat) } diff --git a/logger_test.go b/logger_test.go new file mode 100644 index 000000000..73ba45061 --- /dev/null +++ b/logger_test.go @@ -0,0 +1,42 @@ +package logrus + +import ( + "bytes" + "encoding/json" + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestFieldValueError(t *testing.T) { + buf := &bytes.Buffer{} + l := &Logger{ + Out: buf, + Formatter: new(JSONFormatter), + Hooks: make(LevelHooks), + Level: DebugLevel, + } + l.WithField("func", func() {}).Info("test") + fmt.Println(buf.String()) + var data map[string]interface{} + json.Unmarshal(buf.Bytes(), &data) + _, ok := data[FieldKeyLogrusError] + require.True(t, ok) +} + +func TestNoFieldValueError(t *testing.T) { + buf := &bytes.Buffer{} + l := &Logger{ + Out: buf, + Formatter: new(JSONFormatter), + Hooks: make(LevelHooks), + Level: DebugLevel, + } + l.WithField("str", "str").Info("test") + fmt.Println(buf.String()) + var data map[string]interface{} + json.Unmarshal(buf.Bytes(), &data) + _, ok := data[FieldKeyLogrusError] + require.False(t, ok) +} diff --git a/text_formatter.go b/text_formatter.go index 67fb686c6..d4663b8c2 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -114,7 +114,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { keys = append(keys, k) } - fixedKeys := make([]string, 0, 3+len(entry.Data)) + fixedKeys := make([]string, 0, 4+len(entry.Data)) if !f.DisableTimestamp { fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime)) } @@ -122,6 +122,9 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { if entry.Message != "" { fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyMsg)) } + if entry.err != "" { + fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError)) + } if !f.DisableSorting { if f.SortingFunc == nil { @@ -164,6 +167,8 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { value = entry.Level.String() case f.FieldMap.resolve(FieldKeyMsg): value = entry.Message + case f.FieldMap.resolve(FieldKeyLogrusError): + value = entry.err default: value = entry.Data[key] } From 6981fe482c7d31726b32a212b8a402a993a9d434 Mon Sep 17 00:00:00 2001 From: Albert Salim Date: Sat, 6 Oct 2018 18:08:19 +0800 Subject: [PATCH 395/547] Add option to panic in `test.NewNullLogger` to allow testing of calls to `Fatal*` See #813 --- alt_exit.go | 2 ++ entry.go | 6 +++--- hooks/test/test.go | 14 +++++++++++++- hooks/test/test_test.go | 11 +++++++++++ logger.go | 9 ++++++--- 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/alt_exit.go b/alt_exit.go index 8af90637a..f1bb44cb6 100644 --- a/alt_exit.go +++ b/alt_exit.go @@ -45,6 +45,8 @@ func runHandlers() { } } +type exitFunc func(int) + // Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code) func Exit(code int) { runHandlers() diff --git a/entry.go b/entry.go index ca634a609..225bdb675 100644 --- a/entry.go +++ b/entry.go @@ -198,7 +198,7 @@ func (entry *Entry) Fatal(args ...interface{}) { if entry.Logger.IsLevelEnabled(FatalLevel) { entry.log(FatalLevel, fmt.Sprint(args...)) } - Exit(1) + entry.Logger.Exit(1) } func (entry *Entry) Panic(args ...interface{}) { @@ -246,7 +246,7 @@ func (entry *Entry) Fatalf(format string, args ...interface{}) { if entry.Logger.IsLevelEnabled(FatalLevel) { entry.Fatal(fmt.Sprintf(format, args...)) } - Exit(1) + entry.Logger.Exit(1) } func (entry *Entry) Panicf(format string, args ...interface{}) { @@ -293,7 +293,7 @@ func (entry *Entry) Fatalln(args ...interface{}) { if entry.Logger.IsLevelEnabled(FatalLevel) { entry.Fatal(entry.sprintlnn(args...)) } - Exit(1) + entry.Logger.Exit(1) } func (entry *Entry) Panicln(args ...interface{}) { diff --git a/hooks/test/test.go b/hooks/test/test.go index 234a17dfa..f84fe805f 100644 --- a/hooks/test/test.go +++ b/hooks/test/test.go @@ -39,12 +39,24 @@ func NewLocal(logger *logrus.Logger) *Hook { } +type TestOption func(logger *logrus.Logger) + +func FatalPanics(logger *logrus.Logger) { + logger.Exit = func(code int) { + panic(code) + } +} + // NewNullLogger creates a discarding logger and installs the test hook. -func NewNullLogger() (*logrus.Logger, *Hook) { +func NewNullLogger(options ...TestOption) (*logrus.Logger, *Hook) { logger := logrus.New() logger.Out = ioutil.Discard + for _, option := range options { + option(logger) + } + return logger, NewLocal(logger) } diff --git a/hooks/test/test_test.go b/hooks/test/test_test.go index d6f6d306d..692d36af2 100644 --- a/hooks/test/test_test.go +++ b/hooks/test/test_test.go @@ -71,3 +71,14 @@ func TestLoggingWithHooksRace(t *testing.T) { entries := hook.AllEntries() assert.Equal(100, len(entries)) } + +func TestFatalWithPanic(t *testing.T) { + assert := assert.New(t) + + logger, hook := NewNullLogger(FatalPanics) + + assert.Nil(hook.LastEntry()) + assert.Equal(0, len(hook.Entries)) + + assert.Panics(func() { logger.Fatal("something went wrong") }) +} diff --git a/logger.go b/logger.go index b67bfcbd3..188c6007c 100644 --- a/logger.go +++ b/logger.go @@ -32,6 +32,8 @@ type Logger struct { mu MutexWrap // Reusable empty entry entryPool sync.Pool + // Function to exit the application, defaults to `Exit()` + Exit exitFunc } type MutexWrap struct { @@ -73,6 +75,7 @@ func New() *Logger { Formatter: new(TextFormatter), Hooks: make(LevelHooks), Level: InfoLevel, + Exit: Exit, } } @@ -173,7 +176,7 @@ func (logger *Logger) Fatalf(format string, args ...interface{}) { entry.Fatalf(format, args...) logger.releaseEntry(entry) } - Exit(1) + logger.Exit(1) } func (logger *Logger) Panicf(format string, args ...interface{}) { @@ -236,7 +239,7 @@ func (logger *Logger) Fatal(args ...interface{}) { entry.Fatal(args...) logger.releaseEntry(entry) } - Exit(1) + logger.Exit(1) } func (logger *Logger) Panic(args ...interface{}) { @@ -299,7 +302,7 @@ func (logger *Logger) Fatalln(args ...interface{}) { entry.Fatalln(args...) logger.releaseEntry(entry) } - Exit(1) + logger.Exit(1) } func (logger *Logger) Panicln(args ...interface{}) { From e5e50fca4046ecd1b7b30be4c7c523f1babde444 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sat, 6 Oct 2018 15:21:56 +0200 Subject: [PATCH 396/547] Rationalize os specific build constraints --- terminal_appengine.go | 13 ------------- terminal_bsd.go | 17 ----------------- terminal_linux.go | 21 --------------------- terminal_notwindows.go | 8 ++++++++ 4 files changed, 8 insertions(+), 51 deletions(-) delete mode 100644 terminal_appengine.go delete mode 100644 terminal_bsd.go delete mode 100644 terminal_linux.go create mode 100644 terminal_notwindows.go diff --git a/terminal_appengine.go b/terminal_appengine.go deleted file mode 100644 index 72f679cdb..000000000 --- a/terminal_appengine.go +++ /dev/null @@ -1,13 +0,0 @@ -// Based on ssh/terminal: -// Copyright 2018 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 appengine - -package logrus - -import "io" - -func initTerminal(w io.Writer) { -} diff --git a/terminal_bsd.go b/terminal_bsd.go deleted file mode 100644 index 62ca252d0..000000000 --- a/terminal_bsd.go +++ /dev/null @@ -1,17 +0,0 @@ -// +build darwin freebsd openbsd netbsd dragonfly -// +build !appengine,!js - -package logrus - -import ( - "io" - - "golang.org/x/sys/unix" -) - -const ioctlReadTermios = unix.TIOCGETA - -type Termios unix.Termios - -func initTerminal(w io.Writer) { -} diff --git a/terminal_linux.go b/terminal_linux.go deleted file mode 100644 index 18066f08a..000000000 --- a/terminal_linux.go +++ /dev/null @@ -1,21 +0,0 @@ -// Based on ssh/terminal: -// Copyright 2013 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 !appengine,!js - -package logrus - -import ( - "io" - - "golang.org/x/sys/unix" -) - -const ioctlReadTermios = unix.TCGETS - -type Termios unix.Termios - -func initTerminal(w io.Writer) { -} diff --git a/terminal_notwindows.go b/terminal_notwindows.go new file mode 100644 index 000000000..3dbd23720 --- /dev/null +++ b/terminal_notwindows.go @@ -0,0 +1,8 @@ +// +build !windows + +package logrus + +import "io" + +func initTerminal(w io.Writer) { +} From 1a338358e20f4dd4484a17ef47d6842a4990c447 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Mon, 8 Oct 2018 22:30:39 +0200 Subject: [PATCH 397/547] Update changelog for v1.1.1 release --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 170269608..ff0471869 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.1.1 +This is a bug fix release. + * fix the build break on Solaris + * don't drop a whole trace in JSONFormatter when a field param is a function pointer which can not be serialized + # 1.1.0 This new release introduces: * several fixes: From 587a6d6a6d311ccc9d929b91630657931a0f8d33 Mon Sep 17 00:00:00 2001 From: Albert Salim Date: Wed, 10 Oct 2018 21:54:15 +0800 Subject: [PATCH 398/547] Add a method Exit on Logger that calls `os.Exit` or alternate exit function. This keeps backward compatibility for static declaration of logger that does not specify `ExitFunc` field. --- alt_exit.go | 8 ++++++-- hooks/test/test.go | 14 +------------- hooks/test/test_test.go | 13 +++++++------ logger.go | 14 +++++++++++--- logrus.go | 2 +- 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/alt_exit.go b/alt_exit.go index f1bb44cb6..183db7734 100644 --- a/alt_exit.go +++ b/alt_exit.go @@ -45,11 +45,15 @@ func runHandlers() { } } -type exitFunc func(int) - // Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code) func Exit(code int) { runHandlers() + osExit(code) +} + +type exitFunc func(int) + +func osExit(code int) { os.Exit(code) } diff --git a/hooks/test/test.go b/hooks/test/test.go index f84fe805f..234a17dfa 100644 --- a/hooks/test/test.go +++ b/hooks/test/test.go @@ -39,24 +39,12 @@ func NewLocal(logger *logrus.Logger) *Hook { } -type TestOption func(logger *logrus.Logger) - -func FatalPanics(logger *logrus.Logger) { - logger.Exit = func(code int) { - panic(code) - } -} - // NewNullLogger creates a discarding logger and installs the test hook. -func NewNullLogger(options ...TestOption) (*logrus.Logger, *Hook) { +func NewNullLogger() (*logrus.Logger, *Hook) { logger := logrus.New() logger.Out = ioutil.Discard - for _, option := range options { - option(logger) - } - return logger, NewLocal(logger) } diff --git a/hooks/test/test_test.go b/hooks/test/test_test.go index 692d36af2..636bad512 100644 --- a/hooks/test/test_test.go +++ b/hooks/test/test_test.go @@ -72,13 +72,14 @@ func TestLoggingWithHooksRace(t *testing.T) { assert.Equal(100, len(entries)) } -func TestFatalWithPanic(t *testing.T) { +func TestFatalWithAlternateExit(t *testing.T) { assert := assert.New(t) - logger, hook := NewNullLogger(FatalPanics) - - assert.Nil(hook.LastEntry()) - assert.Equal(0, len(hook.Entries)) + logger, hook := NewNullLogger() + logger.ExitFunc = func(code int) {} - assert.Panics(func() { logger.Fatal("something went wrong") }) + logger.Fatal("something went very wrong") + assert.Equal(logrus.FatalLevel, hook.LastEntry().Level) + assert.Equal("something went very wrong", hook.LastEntry().Message) + assert.Equal(1, len(hook.Entries)) } diff --git a/logger.go b/logger.go index 188c6007c..364819eec 100644 --- a/logger.go +++ b/logger.go @@ -32,8 +32,8 @@ type Logger struct { mu MutexWrap // Reusable empty entry entryPool sync.Pool - // Function to exit the application, defaults to `Exit()` - Exit exitFunc + // Function to exit the application, defaults to `osExit()` + ExitFunc exitFunc } type MutexWrap struct { @@ -75,7 +75,7 @@ func New() *Logger { Formatter: new(TextFormatter), Hooks: make(LevelHooks), Level: InfoLevel, - Exit: Exit, + ExitFunc: osExit, } } @@ -313,6 +313,14 @@ func (logger *Logger) Panicln(args ...interface{}) { } } +func (logger *Logger) Exit(code int) { + runHandlers() + if logger.ExitFunc == nil { + logger.ExitFunc = osExit + } + logger.ExitFunc(code) +} + //When file is opened with appending mode, it's safe to //write concurrently to a file (within 4k message on Linux). //In these cases user can choose to disable the lock. diff --git a/logrus.go b/logrus.go index fa0b9dea8..6fff5065f 100644 --- a/logrus.go +++ b/logrus.go @@ -69,7 +69,7 @@ const ( // PanicLevel level, highest level of severity. Logs and then calls panic with the // message passed to Debug, Info, ... PanicLevel Level = iota - // FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the + // FatalLevel level. Logs and then calls `logger.Exit(1)`. It will exit even if the // logging level is set to Panic. FatalLevel // ErrorLevel level. Logs. Used for errors that should definitely be noted. From 2a01439097cc799b20270b5629f02e78557b46da Mon Sep 17 00:00:00 2001 From: Albert Salim Date: Wed, 10 Oct 2018 21:57:58 +0800 Subject: [PATCH 399/547] Remove unnecessary wrapper function on `os.Exit` --- alt_exit.go | 6 ------ logger.go | 6 ++++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/alt_exit.go b/alt_exit.go index 183db7734..8af90637a 100644 --- a/alt_exit.go +++ b/alt_exit.go @@ -48,12 +48,6 @@ func runHandlers() { // Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code) func Exit(code int) { runHandlers() - osExit(code) -} - -type exitFunc func(int) - -func osExit(code int) { os.Exit(code) } diff --git a/logger.go b/logger.go index 364819eec..21f49ecad 100644 --- a/logger.go +++ b/logger.go @@ -36,6 +36,8 @@ type Logger struct { ExitFunc exitFunc } +type exitFunc func(int) + type MutexWrap struct { lock sync.Mutex disabled bool @@ -75,7 +77,7 @@ func New() *Logger { Formatter: new(TextFormatter), Hooks: make(LevelHooks), Level: InfoLevel, - ExitFunc: osExit, + ExitFunc: os.Exit, } } @@ -316,7 +318,7 @@ func (logger *Logger) Panicln(args ...interface{}) { func (logger *Logger) Exit(code int) { runHandlers() if logger.ExitFunc == nil { - logger.ExitFunc = osExit + logger.ExitFunc = os.Exit } logger.ExitFunc(code) } From bf2a167f6f172cf083db45e7f4250fada1ab5ebf Mon Sep 17 00:00:00 2001 From: Albert Salim Date: Wed, 10 Oct 2018 21:59:03 +0800 Subject: [PATCH 400/547] Fix typo in comment --- logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logger.go b/logger.go index 21f49ecad..1c934ed22 100644 --- a/logger.go +++ b/logger.go @@ -32,7 +32,7 @@ type Logger struct { mu MutexWrap // Reusable empty entry entryPool sync.Pool - // Function to exit the application, defaults to `osExit()` + // Function to exit the application, defaults to `os.Exit()` ExitFunc exitFunc } From da80f56a1e7ad018fa98c5f01c09ae2d37606255 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Fri, 12 Oct 2018 06:53:57 +0200 Subject: [PATCH 401/547] Attempt to fix build break on aix --- terminal_check_aix.go | 9 +++++++++ terminal_check_notappengine.go | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 terminal_check_aix.go diff --git a/terminal_check_aix.go b/terminal_check_aix.go new file mode 100644 index 000000000..04fdb7ba3 --- /dev/null +++ b/terminal_check_aix.go @@ -0,0 +1,9 @@ +// +build !appengine,!js,!windows,aix + +package logrus + +import "io" + +func checkIfTerminal(w io.Writer) bool { + return false +} diff --git a/terminal_check_notappengine.go b/terminal_check_notappengine.go index cf309d6fb..d46556509 100644 --- a/terminal_check_notappengine.go +++ b/terminal_check_notappengine.go @@ -1,4 +1,4 @@ -// +build !appengine,!js,!windows +// +build !appengine,!js,!windows,!aix package logrus From 1dcfd11ea7d419053581d8eca94a34d621d2479d Mon Sep 17 00:00:00 2001 From: David Bariod Date: Mon, 15 Oct 2018 21:20:03 +0200 Subject: [PATCH 402/547] Add an example for tracing global variable with hook --- example_global_hook_test.go | 37 +++++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 2 ++ 3 files changed, 40 insertions(+) create mode 100644 example_global_hook_test.go diff --git a/example_global_hook_test.go b/example_global_hook_test.go new file mode 100644 index 000000000..df1584c93 --- /dev/null +++ b/example_global_hook_test.go @@ -0,0 +1,37 @@ +package logrus_test + +import ( + "github.com/sirupsen/logrus" + "os" +) + +var ( + mystring string +) + +type GlobalHook struct { +} + +func (h *GlobalHook) Levels() []logrus.Level { + return logrus.AllLevels +} + +func (h *GlobalHook) Fire(e *logrus.Entry) error { + e.Data["mystring"] = mystring + return nil +} + +func Example() { + l := logrus.New() + l.Out = os.Stdout + l.Formatter = &logrus.TextFormatter{DisableTimestamp: true} + l.Formatter.(*logrus.TextFormatter).DisableTimestamp = true + l.AddHook(&GlobalHook{}) + mystring = "first value" + l.Info("first log") + mystring = "another value" + l.Info("second log") + // Output: + // level=info msg="first log" mystring="first value" + // level=info msg="second log" mystring="another value" +} diff --git a/go.mod b/go.mod index f4fed02fb..79f11b79f 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.1.1 // indirect github.com/stretchr/testify v1.2.2 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 diff --git a/go.sum b/go.sum index 1f0d71964..16cab75c5 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f26 github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I= From b5f5425dc3d12fdee894f9eb009b75372dea462b Mon Sep 17 00:00:00 2001 From: David Bariod Date: Mon, 15 Oct 2018 21:32:20 +0200 Subject: [PATCH 403/547] disable colors on hook example --- example_global_hook_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/example_global_hook_test.go b/example_global_hook_test.go index df1584c93..c81e448c2 100644 --- a/example_global_hook_test.go +++ b/example_global_hook_test.go @@ -24,8 +24,7 @@ func (h *GlobalHook) Fire(e *logrus.Entry) error { func Example() { l := logrus.New() l.Out = os.Stdout - l.Formatter = &logrus.TextFormatter{DisableTimestamp: true} - l.Formatter.(*logrus.TextFormatter).DisableTimestamp = true + l.Formatter = &logrus.TextFormatter{DisableTimestamp: true, DisableColors: true} l.AddHook(&GlobalHook{}) mystring = "first value" l.Info("first log") From 04bda4ee19be857f5c3db080cfbaa02a39b7fcf8 Mon Sep 17 00:00:00 2001 From: drampull Date: Fri, 20 Oct 2017 08:40:54 -0400 Subject: [PATCH 404/547] Added TRACE level logging. --- .gitignore | 1 + entry.go | 18 ++++++++++++++++++ example_basic_test.go | 8 +++++++- exported.go | 15 +++++++++++++++ hook_test.go | 2 ++ hooks/syslog/syslog.go | 2 +- logger.go | 24 ++++++++++++++++++++++++ logrus.go | 10 ++++++++++ text_formatter.go | 2 +- writer.go | 2 ++ 10 files changed, 81 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 66be63a00..6b7d7d1e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ logrus +vendor diff --git a/entry.go b/entry.go index 225bdb675..eb4ae5c83 100644 --- a/entry.go +++ b/entry.go @@ -168,6 +168,12 @@ func (entry *Entry) Debug(args ...interface{}) { } } +func (entry *Entry) Trace(args ...interface{}) { + if entry.Logger.IsLevelEnabled(TraceLevel) { + entry.log(TraceLevel, fmt.Sprint(args...)) + } +} + func (entry *Entry) Print(args ...interface{}) { entry.Info(args...) } @@ -216,6 +222,12 @@ func (entry *Entry) Debugf(format string, args ...interface{}) { } } +func (entry *Entry) Tracef(format string, args ...interface{}) { + if entry.Logger.IsLevelEnabled(TraceLevel) { + entry.Trace(fmt.Sprintf(format, args...)) + } +} + func (entry *Entry) Infof(format string, args ...interface{}) { if entry.Logger.IsLevelEnabled(InfoLevel) { entry.Info(fmt.Sprintf(format, args...)) @@ -257,6 +269,12 @@ func (entry *Entry) Panicf(format string, args ...interface{}) { // Entry Println family functions +func (entry *Entry) Traceln(args ...interface{}) { + if entry.Logger.IsLevelEnabled(TraceLevel) { + entry.Trace(entry.sprintlnn(args...)) + } +} + func (entry *Entry) Debugln(args ...interface{}) { if entry.Logger.IsLevelEnabled(DebugLevel) { entry.Debug(entry.sprintlnn(args...)) diff --git a/example_basic_test.go b/example_basic_test.go index 5f3849bff..9ff56555b 100644 --- a/example_basic_test.go +++ b/example_basic_test.go @@ -12,7 +12,7 @@ func Example_basic() { log.Formatter = new(logrus.TextFormatter) //default log.Formatter.(*logrus.TextFormatter).DisableColors = true // remove colors log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true // remove timestamp from test output - log.Level = logrus.DebugLevel + log.Level = logrus.TraceLevel log.Out = os.Stdout // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666) @@ -37,6 +37,11 @@ func Example_basic() { } }() + log.WithFields(logrus.Fields{ + "animal": "walrus", + "number": 0, + }).Trace("Went to the beach") + log.WithFields(logrus.Fields{ "animal": "walrus", "number": 8, @@ -62,6 +67,7 @@ func Example_basic() { }).Panic("It's over 9000!") // Output: + // level=trace msg="Went to the beach" animal=walrus number=0 // 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 diff --git a/exported.go b/exported.go index fb2a7a1f0..fb261aacd 100644 --- a/exported.go +++ b/exported.go @@ -77,6 +77,11 @@ func WithTime(t time.Time) *Entry { return std.WithTime(t) } +// Trace logs a message at level Trace on the standard logger. +func Trace(args ...interface{}) { + std.Trace(args...) +} + // Debug logs a message at level Debug on the standard logger. func Debug(args ...interface{}) { std.Debug(args...) @@ -117,6 +122,11 @@ func Fatal(args ...interface{}) { std.Fatal(args...) } +// Tracef logs a message at level Debug on the standard logger. +func Tracef(format string, args ...interface{}) { + std.Tracef(format, args...) +} + // Debugf logs a message at level Debug on the standard logger. func Debugf(format string, args ...interface{}) { std.Debugf(format, args...) @@ -157,6 +167,11 @@ func Fatalf(format string, args ...interface{}) { std.Fatalf(format, args...) } +// Traceln logs a message at level Debug on the standard logger. +func Traceln(args ...interface{}) { + std.Traceln(args...) +} + // Debugln logs a message at level Debug on the standard logger. func Debugln(args ...interface{}) { std.Debugln(args...) diff --git a/hook_test.go b/hook_test.go index 80b93b8c6..7196e990e 100644 --- a/hook_test.go +++ b/hook_test.go @@ -21,6 +21,7 @@ func (hook *TestHook) Fire(entry *Entry) error { func (hook *TestHook) Levels() []Level { return []Level{ + TraceLevel, DebugLevel, InfoLevel, WarnLevel, @@ -53,6 +54,7 @@ func (hook *ModifyHook) Fire(entry *Entry) error { func (hook *ModifyHook) Levels() []Level { return []Level{ + TraceLevel, DebugLevel, InfoLevel, WarnLevel, diff --git a/hooks/syslog/syslog.go b/hooks/syslog/syslog.go index 329ce0d60..02b8df380 100644 --- a/hooks/syslog/syslog.go +++ b/hooks/syslog/syslog.go @@ -43,7 +43,7 @@ func (hook *SyslogHook) Fire(entry *logrus.Entry) error { return hook.Writer.Warning(line) case logrus.InfoLevel: return hook.Writer.Info(line) - case logrus.DebugLevel: + case logrus.DebugLevel, logrus.TraceLevel: return hook.Writer.Debug(line) default: return nil diff --git a/logger.go b/logger.go index 1c934ed22..52f1761d0 100644 --- a/logger.go +++ b/logger.go @@ -126,6 +126,14 @@ func (logger *Logger) WithTime(t time.Time) *Entry { return entry.WithTime(t) } +func (logger *Logger) Tracef(format string, args ...interface{}) { + if logger.IsLevelEnabled(TraceLevel) { + entry := logger.newEntry() + entry.Tracef(format, args...) + logger.releaseEntry(entry) + } +} + func (logger *Logger) Debugf(format string, args ...interface{}) { if logger.IsLevelEnabled(DebugLevel) { entry := logger.newEntry() @@ -189,6 +197,14 @@ func (logger *Logger) Panicf(format string, args ...interface{}) { } } +func (logger *Logger) Trace(args ...interface{}) { + if logger.IsLevelEnabled(TraceLevel) { + entry := logger.newEntry() + entry.Trace(args...) + logger.releaseEntry(entry) + } +} + func (logger *Logger) Debug(args ...interface{}) { if logger.IsLevelEnabled(DebugLevel) { entry := logger.newEntry() @@ -252,6 +268,14 @@ func (logger *Logger) Panic(args ...interface{}) { } } +func (logger *Logger) Traceln(args ...interface{}) { + if logger.IsLevelEnabled(TraceLevel) { + entry := logger.newEntry() + entry.Traceln(args...) + logger.releaseEntry(entry) + } +} + func (logger *Logger) Debugln(args ...interface{}) { if logger.IsLevelEnabled(DebugLevel) { entry := logger.newEntry() diff --git a/logrus.go b/logrus.go index 6fff5065f..601d5e06a 100644 --- a/logrus.go +++ b/logrus.go @@ -15,6 +15,8 @@ type Level uint32 // Convert the Level to a string. E.g. PanicLevel becomes "panic". func (level Level) String() string { switch level { + case TraceLevel: + return "trace" case DebugLevel: return "debug" case InfoLevel: @@ -47,6 +49,8 @@ func ParseLevel(lvl string) (Level, error) { return InfoLevel, nil case "debug": return DebugLevel, nil + case "trace": + return TraceLevel, nil } var l Level @@ -61,6 +65,7 @@ var AllLevels = []Level{ WarnLevel, InfoLevel, DebugLevel, + TraceLevel, } // These are the different logging levels. You can set the logging level to log @@ -82,6 +87,8 @@ const ( InfoLevel // DebugLevel level. Usually only enabled when debugging. Very verbose logging. DebugLevel + // TraceLevel level. Usually only enabled when debugging. Very verbose logging. Usually reserved for message traces + TraceLevel ) // Won't compile if StdLogger can't be realized by a log.Logger @@ -114,6 +121,7 @@ type FieldLogger interface { WithFields(fields Fields) *Entry WithError(err error) *Entry + Tracef(format string, args ...interface{}) Debugf(format string, args ...interface{}) Infof(format string, args ...interface{}) Printf(format string, args ...interface{}) @@ -123,6 +131,7 @@ type FieldLogger interface { Fatalf(format string, args ...interface{}) Panicf(format string, args ...interface{}) + Trace(args ...interface{}) Debug(args ...interface{}) Info(args ...interface{}) Print(args ...interface{}) @@ -132,6 +141,7 @@ type FieldLogger interface { Fatal(args ...interface{}) Panic(args ...interface{}) + Traceln(args ...interface{}) Debugln(args ...interface{}) Infoln(args ...interface{}) Println(args ...interface{}) diff --git a/text_formatter.go b/text_formatter.go index d4663b8c2..74dffcfcf 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -183,7 +183,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) { var levelColor int switch entry.Level { - case DebugLevel: + case DebugLevel, TraceLevel: levelColor = gray case WarnLevel: levelColor = yellow diff --git a/writer.go b/writer.go index 7bdebedc6..9e1f75135 100644 --- a/writer.go +++ b/writer.go @@ -24,6 +24,8 @@ func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { var printFunc func(args ...interface{}) switch level { + case TraceLevel: + printFunc = entry.Trace case DebugLevel: printFunc = entry.Debug case InfoLevel: From d814433f980532b7c32095a5f4c38bfa8e1afdd3 Mon Sep 17 00:00:00 2001 From: Giedrius Dubinskas Date: Mon, 2 Oct 2017 19:38:29 +0300 Subject: [PATCH 405/547] Add Trace level logging --- exported.go | 4 ++-- logrus.go | 3 ++- logrus_test.go | 9 +++++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/exported.go b/exported.go index fb261aacd..b9f1c9d8b 100644 --- a/exported.go +++ b/exported.go @@ -122,7 +122,7 @@ func Fatal(args ...interface{}) { std.Fatal(args...) } -// Tracef logs a message at level Debug on the standard logger. +// Tracef logs a message at level Trace on the standard logger. func Tracef(format string, args ...interface{}) { std.Tracef(format, args...) } @@ -167,7 +167,7 @@ func Fatalf(format string, args ...interface{}) { std.Fatalf(format, args...) } -// Traceln logs a message at level Debug on the standard logger. +// Traceln logs a message at level Trace on the standard logger. func Traceln(args ...interface{}) { std.Traceln(args...) } diff --git a/logrus.go b/logrus.go index 601d5e06a..d657c4140 100644 --- a/logrus.go +++ b/logrus.go @@ -87,7 +87,8 @@ const ( InfoLevel // DebugLevel level. Usually only enabled when debugging. Very verbose logging. DebugLevel - // TraceLevel level. Usually only enabled when debugging. Very verbose logging. Usually reserved for message traces + // TraceLevel level. Usually only enabled when debugging. Very verbose logging. + // Usually reserved for message traces. TraceLevel ) diff --git a/logrus_test.go b/logrus_test.go index 97d15d7fd..210feefda 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -303,6 +303,7 @@ func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) { } func TestConvertLevelToString(t *testing.T) { + assert.Equal(t, "trace", TraceLevel.String()) assert.Equal(t, "debug", DebugLevel.String()) assert.Equal(t, "info", InfoLevel.String()) assert.Equal(t, "warning", WarnLevel.String()) @@ -368,6 +369,14 @@ func TestParseLevel(t *testing.T) { assert.Nil(t, err) assert.Equal(t, DebugLevel, l) + l, err = ParseLevel("trace") + assert.Nil(t, err) + assert.Equal(t, TraceLevel, l) + + l, err = ParseLevel("TRACE") + assert.Nil(t, err) + assert.Equal(t, TraceLevel, l) + l, err = ParseLevel("invalid") assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error()) } From 1d0fcccfd326730228ad248cb3ac15c0d08847d5 Mon Sep 17 00:00:00 2001 From: Maxim Korolyov Date: Tue, 28 Aug 2018 18:13:29 +0300 Subject: [PATCH 406/547] Added trace log level. --- README.md | 3 ++- entry.go | 34 +++++++++++++++++----------------- logrus.go | 3 +-- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 072e99be3..f088432b4 100644 --- a/README.md +++ b/README.md @@ -246,9 +246,10 @@ A list of currently known of service hook can be found in this wiki [page](https #### Level logging -Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic. +Logrus has seven logging levels: Trace, Debug, Info, Warning, Error, Fatal and Panic. ```go +log.Trace("Something very low level.") log.Debug("Useful debugging information.") log.Info("Something noteworthy happened!") log.Warn("You should probably take a look at this.") diff --git a/entry.go b/entry.go index eb4ae5c83..a76be7681 100644 --- a/entry.go +++ b/entry.go @@ -23,9 +23,9 @@ func init() { var ErrorKey = "error" // An entry is the final or intermediate Logrus logging entry. It contains all -// the fields passed with WithField{,s}. It's finally logged when Debug, Info, -// Warn, Error, Fatal or Panic is called on it. These objects can be reused and -// passed around as much as you wish to avoid field duplication. +// the fields passed with WithField{,s}. It's finally logged when Trace, Debug, +// Info, Warn, Error, Fatal or Panic is called on it. These objects can be +// reused and passed around as much as you wish to avoid field duplication. type Entry struct { Logger *Logger @@ -35,11 +35,11 @@ type Entry struct { // Time at which the log entry was created Time time.Time - // Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic + // Level the log entry was logged at: Trace, 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 + // Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic Message string // When formatter is called in entry.log(), a Buffer may be set to entry @@ -162,18 +162,18 @@ func (entry *Entry) write() { } } -func (entry *Entry) Debug(args ...interface{}) { - if entry.Logger.IsLevelEnabled(DebugLevel) { - entry.log(DebugLevel, fmt.Sprint(args...)) - } -} - func (entry *Entry) Trace(args ...interface{}) { if entry.Logger.IsLevelEnabled(TraceLevel) { entry.log(TraceLevel, fmt.Sprint(args...)) } } +func (entry *Entry) Debug(args ...interface{}) { + if entry.Logger.IsLevelEnabled(DebugLevel) { + entry.log(DebugLevel, fmt.Sprint(args...)) + } +} + func (entry *Entry) Print(args ...interface{}) { entry.Info(args...) } @@ -216,18 +216,18 @@ func (entry *Entry) Panic(args ...interface{}) { // Entry Printf family functions -func (entry *Entry) Debugf(format string, args ...interface{}) { - if entry.Logger.IsLevelEnabled(DebugLevel) { - entry.Debug(fmt.Sprintf(format, args...)) - } -} - func (entry *Entry) Tracef(format string, args ...interface{}) { if entry.Logger.IsLevelEnabled(TraceLevel) { entry.Trace(fmt.Sprintf(format, args...)) } } +func (entry *Entry) Debugf(format string, args ...interface{}) { + if entry.Logger.IsLevelEnabled(DebugLevel) { + entry.Debug(fmt.Sprintf(format, args...)) + } +} + func (entry *Entry) Infof(format string, args ...interface{}) { if entry.Logger.IsLevelEnabled(InfoLevel) { entry.Info(fmt.Sprintf(format, args...)) diff --git a/logrus.go b/logrus.go index d657c4140..8834b5b20 100644 --- a/logrus.go +++ b/logrus.go @@ -87,8 +87,7 @@ const ( InfoLevel // DebugLevel level. Usually only enabled when debugging. Very verbose logging. DebugLevel - // TraceLevel level. Usually only enabled when debugging. Very verbose logging. - // Usually reserved for message traces. + // TraceLevel level. Designates finer-grained informational events than the Debug. TraceLevel ) From 3fd31d324039f42a64dbbb2c8ef2c89c54d729f9 Mon Sep 17 00:00:00 2001 From: Loren Osborn Date: Thu, 18 Oct 2018 09:33:43 -0700 Subject: [PATCH 407/547] Addresses @stevvooe's backward compatibility concerns. --- logrus.go | 12 +++++++++--- logrus_test.go | 7 +++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/logrus.go b/logrus.go index 8834b5b20..03046b648 100644 --- a/logrus.go +++ b/logrus.go @@ -121,7 +121,6 @@ type FieldLogger interface { WithFields(fields Fields) *Entry WithError(err error) *Entry - Tracef(format string, args ...interface{}) Debugf(format string, args ...interface{}) Infof(format string, args ...interface{}) Printf(format string, args ...interface{}) @@ -131,7 +130,6 @@ type FieldLogger interface { Fatalf(format string, args ...interface{}) Panicf(format string, args ...interface{}) - Trace(args ...interface{}) Debug(args ...interface{}) Info(args ...interface{}) Print(args ...interface{}) @@ -141,7 +139,6 @@ type FieldLogger interface { Fatal(args ...interface{}) Panic(args ...interface{}) - Traceln(args ...interface{}) Debugln(args ...interface{}) Infoln(args ...interface{}) Println(args ...interface{}) @@ -158,3 +155,12 @@ type FieldLogger interface { // IsFatalEnabled() bool // IsPanicEnabled() bool } + +// Ext1FieldLogger (the first extension to FieldLogger) is superfluous, it is +// here for consistancy. Do not use. Use Logger or Entry instead. +type Ext1FieldLogger interface { + FieldLogger + Tracef(format string, args ...interface{}) + Trace(args ...interface{}) + Traceln(args ...interface{}) +} diff --git a/logrus_test.go b/logrus_test.go index 210feefda..748f0ba1e 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -453,9 +453,12 @@ func TestReplaceHooks(t *testing.T) { } // Compile test -func TestLogrusInterface(t *testing.T) { +func TestLogrusInterfaces(t *testing.T) { var buffer bytes.Buffer - fn := func(l FieldLogger) { + // This verifies FieldLogger and Ext1FieldLogger work as designed. + // Please don't use them. Use Logger and Entry directly. + fn := func(xl Ext1FieldLogger) { + var l FieldLogger = xl b := l.WithField("key", "value") b.Debug("Test") } From 2df5f1ad858d3f75d19e2fc1fc9ade331675115e Mon Sep 17 00:00:00 2001 From: Loren Osborn Date: Thu, 18 Oct 2018 22:35:59 -0700 Subject: [PATCH 408/547] PR#844: Added Trace to TestLogLevelEnabled() (requested by @dgsb) --- logrus_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/logrus_test.go b/logrus_test.go index 748f0ba1e..ccdf5cc56 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -506,6 +506,7 @@ func TestLogLevelEnabled(t *testing.T) { assert.Equal(t, false, log.IsLevelEnabled(WarnLevel)) assert.Equal(t, false, log.IsLevelEnabled(InfoLevel)) assert.Equal(t, false, log.IsLevelEnabled(DebugLevel)) + assert.Equal(t, false, log.IsLevelEnabled(TraceLevel)) log.SetLevel(FatalLevel) assert.Equal(t, true, log.IsLevelEnabled(PanicLevel)) @@ -514,6 +515,7 @@ func TestLogLevelEnabled(t *testing.T) { assert.Equal(t, false, log.IsLevelEnabled(WarnLevel)) assert.Equal(t, false, log.IsLevelEnabled(InfoLevel)) assert.Equal(t, false, log.IsLevelEnabled(DebugLevel)) + assert.Equal(t, false, log.IsLevelEnabled(TraceLevel)) log.SetLevel(ErrorLevel) assert.Equal(t, true, log.IsLevelEnabled(PanicLevel)) @@ -522,6 +524,7 @@ func TestLogLevelEnabled(t *testing.T) { assert.Equal(t, false, log.IsLevelEnabled(WarnLevel)) assert.Equal(t, false, log.IsLevelEnabled(InfoLevel)) assert.Equal(t, false, log.IsLevelEnabled(DebugLevel)) + assert.Equal(t, false, log.IsLevelEnabled(TraceLevel)) log.SetLevel(WarnLevel) assert.Equal(t, true, log.IsLevelEnabled(PanicLevel)) @@ -530,6 +533,7 @@ func TestLogLevelEnabled(t *testing.T) { assert.Equal(t, true, log.IsLevelEnabled(WarnLevel)) assert.Equal(t, false, log.IsLevelEnabled(InfoLevel)) assert.Equal(t, false, log.IsLevelEnabled(DebugLevel)) + assert.Equal(t, false, log.IsLevelEnabled(TraceLevel)) log.SetLevel(InfoLevel) assert.Equal(t, true, log.IsLevelEnabled(PanicLevel)) @@ -538,6 +542,7 @@ func TestLogLevelEnabled(t *testing.T) { assert.Equal(t, true, log.IsLevelEnabled(WarnLevel)) assert.Equal(t, true, log.IsLevelEnabled(InfoLevel)) assert.Equal(t, false, log.IsLevelEnabled(DebugLevel)) + assert.Equal(t, false, log.IsLevelEnabled(TraceLevel)) log.SetLevel(DebugLevel) assert.Equal(t, true, log.IsLevelEnabled(PanicLevel)) @@ -546,4 +551,14 @@ func TestLogLevelEnabled(t *testing.T) { assert.Equal(t, true, log.IsLevelEnabled(WarnLevel)) assert.Equal(t, true, log.IsLevelEnabled(InfoLevel)) assert.Equal(t, true, log.IsLevelEnabled(DebugLevel)) + assert.Equal(t, false, log.IsLevelEnabled(TraceLevel)) + + log.SetLevel(TraceLevel) + assert.Equal(t, true, log.IsLevelEnabled(PanicLevel)) + assert.Equal(t, true, log.IsLevelEnabled(FatalLevel)) + assert.Equal(t, true, log.IsLevelEnabled(ErrorLevel)) + assert.Equal(t, true, log.IsLevelEnabled(WarnLevel)) + assert.Equal(t, true, log.IsLevelEnabled(InfoLevel)) + assert.Equal(t, true, log.IsLevelEnabled(DebugLevel)) + assert.Equal(t, true, log.IsLevelEnabled(TraceLevel)) } From bce06ebc406d083f5b05a1702e476b88c38da8e4 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sun, 21 Oct 2018 11:53:14 +0200 Subject: [PATCH 409/547] Fix the version of windows coloring library dependency --- go.mod | 2 +- go.sum | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 79f11b79f..94574cc63 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/sirupsen/logrus require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe + github.com/konsorten/go-windows-terminal-sequences v1.0.1 github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/objx v0.1.1 // indirect github.com/stretchr/testify v1.2.2 diff --git a/go.sum b/go.sum index 16cab75c5..133d34ae1 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe h1:CHRGQ8V7OlCYtwaKPJi3iA7J+YdNKdo8j7nG5IgDhjs= github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= From 76088bfd5d10dad4359a32378dc47c4abe9c9227 Mon Sep 17 00:00:00 2001 From: Shun Yanaura Date: Fri, 19 Oct 2018 20:35:56 +0900 Subject: [PATCH 410/547] Make logrus.Level implement encoding.TextUnmarshaler --- logrus.go | 12 ++++++++++++ logrus_test.go | 13 +++++++++++++ 2 files changed, 25 insertions(+) diff --git a/logrus.go b/logrus.go index 6fff5065f..5dab4d4fe 100644 --- a/logrus.go +++ b/logrus.go @@ -53,6 +53,18 @@ func ParseLevel(lvl string) (Level, error) { return l, fmt.Errorf("not a valid logrus Level: %q", lvl) } +// UnmarshalText implements encoding.TextUnmarshaler. +func (level *Level) UnmarshalText(text []byte) error { + l, err := ParseLevel(string(text)) + if err != nil { + return err + } + + *level = Level(l) + + return nil +} + // A constant exposing all logging levels var AllLevels = []Level{ PanicLevel, diff --git a/logrus_test.go b/logrus_test.go index 97d15d7fd..87250136b 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -372,6 +372,19 @@ func TestParseLevel(t *testing.T) { assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error()) } +func TestUnmarshalText(t *testing.T) { + var u Level + for _, level := range AllLevels { + t.Run(level.String(), func(t *testing.T) { + assert.NoError(t, u.UnmarshalText([]byte(level.String()))) + assert.Equal(t, level, u) + }) + } + t.Run("invalid", func(t *testing.T) { + assert.Error(t, u.UnmarshalText([]byte("invalid"))) + }) +} + func TestGetSetLevelRace(t *testing.T) { wg := sync.WaitGroup{} for i := 0; i < 100; i++ { From 0e679a6a55efd27d84042ad17afe6cf1ee877510 Mon Sep 17 00:00:00 2001 From: Fabien Meurillon Date: Wed, 24 Oct 2018 11:03:07 +0200 Subject: [PATCH 411/547] Add GELF to third party formatters --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f088432b4..e3d254294 100644 --- a/README.md +++ b/README.md @@ -330,6 +330,7 @@ The built-in logging formatters are: Third party logging formatters: * [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine. +* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html). * [`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 b7189c1a487fe50f9d27b173c330b635951c0f29 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sat, 27 Oct 2018 15:19:06 +0200 Subject: [PATCH 412/547] add a SetReportCaller on Logger object --- exported.go | 4 +--- logger.go | 6 ++++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/exported.go b/exported.go index 979582d4a..7342613c3 100644 --- a/exported.go +++ b/exported.go @@ -27,9 +27,7 @@ func SetFormatter(formatter Formatter) { // SetReportCaller sets whether the standard logger will include the calling // method as a field. func SetReportCaller(include bool) { - std.mu.Lock() - defer std.mu.Unlock() - std.ReportCaller = include + std.SetReportCaller(include) } // SetLevel sets the standard logger level. diff --git a/logger.go b/logger.go index 68fca98a2..5ceca0eab 100644 --- a/logger.go +++ b/logger.go @@ -399,6 +399,12 @@ func (logger *Logger) SetOutput(output io.Writer) { logger.Out = output } +func (logger *Logger) SetReportCaller(reportCaller bool) { + logger.mu.Lock() + defer logger.mu.Unlock() + logger.ReportCaller = reportCaller +} + // ReplaceHooks replaces the logger hooks and returns the old ones func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks { logger.mu.Lock() From 54abc2cc7ab88f4f914d40bebe01fab6beb4ff57 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sat, 27 Oct 2018 15:21:30 +0200 Subject: [PATCH 413/547] Use a sync.Once to init the reportCaller data --- entry.go | 30 +++++++++++++++++++----------- logrus_test.go | 4 ++-- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/entry.go b/entry.go index df375506b..5f40c58f3 100644 --- a/entry.go +++ b/entry.go @@ -11,16 +11,23 @@ import ( "time" ) -var bufferPool *sync.Pool +var ( + bufferPool *sync.Pool -// qualified package name, cached at first use -var LogrusPackage string + // qualified package name, cached at first use + logrusPackage string -// Positions in the call stack when tracing to report the calling method -var minimumCallerDepth int + // Positions in the call stack when tracing to report the calling method + minimumCallerDepth int -const maximumCallerDepth int = 25 -const knownLogrusFrames int = 4 + // Used for caller information initialisation + callerInitOnce sync.Once +) + +const ( + maximumCallerDepth int = 25 + knownLogrusFrames int = 4 +) func init() { bufferPool = &sync.Pool{ @@ -143,19 +150,20 @@ func getCaller() (method string) { depth := runtime.Callers(minimumCallerDepth, pcs) // cache this package's fully-qualified name - if LogrusPackage == "" { - LogrusPackage = getPackageName(runtime.FuncForPC(pcs[0]).Name()) + callerInitOnce.Do(func() { + logrusPackage = getPackageName(runtime.FuncForPC(pcs[0]).Name()) // now that we have the cache, we can skip a minimum count of known-logrus functions + // XXX this is dubious, the number of frames may vary store an entry in a logger interface minimumCallerDepth = knownLogrusFrames - } + }) for i := 0; i < depth; i++ { fullFuncName := runtime.FuncForPC(pcs[i]).Name() pkg := getPackageName(fullFuncName) // If the caller isn't part of this package, we're done - if pkg != LogrusPackage { + if pkg != logrusPackage { return fullFuncName } } diff --git a/logrus_test.go b/logrus_test.go index d82936900..2fe3653b1 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -92,7 +92,7 @@ func logSomething(t *testing.T, message string) Fields { logger.ReportCaller = true // override the filter to allow reporting of functions within the logrus package - LogrusPackage = "bogusForTesting" + logrusPackage = "bogusForTesting" entry := logger.WithFields(Fields{ "foo": "bar", @@ -104,7 +104,7 @@ func logSomething(t *testing.T, message string) Fields { assert.Nil(t, err) // now clear the override so as not to mess with other usage - LogrusPackage = "" + logrusPackage = "" return fields } From cf2525140cfc2292b98848c1832e7dbde61b4f27 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sun, 28 Oct 2018 10:12:11 +0100 Subject: [PATCH 414/547] store a runtime.Frame in Entry instead of the caller function name --- entry.go | 16 ++++++++-------- json_formatter.go | 2 +- json_formatter_test.go | 7 ++++--- text_formatter.go | 2 +- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/entry.go b/entry.go index 5f40c58f3..cc85d3aab 100644 --- a/entry.go +++ b/entry.go @@ -61,7 +61,7 @@ type Entry struct { Level Level // Calling method, with package name - Caller string + Caller *runtime.Frame // Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic Message string @@ -144,10 +144,11 @@ func getPackageName(f string) string { } // getCaller retrieves the name of the first non-logrus calling function -func getCaller() (method string) { +func getCaller() *runtime.Frame { // Restrict the lookback frames to avoid runaway lookups pcs := make([]uintptr, maximumCallerDepth) depth := runtime.Callers(minimumCallerDepth, pcs) + frames := runtime.CallersFrames(pcs[:depth]) // cache this package's fully-qualified name callerInitOnce.Do(func() { @@ -158,24 +159,23 @@ func getCaller() (method string) { minimumCallerDepth = knownLogrusFrames }) - for i := 0; i < depth; i++ { - fullFuncName := runtime.FuncForPC(pcs[i]).Name() - pkg := getPackageName(fullFuncName) + for f, again := frames.Next(); again; f, again = frames.Next() { + pkg := getPackageName(f.Function) // If the caller isn't part of this package, we're done if pkg != logrusPackage { - return fullFuncName + return &f } } // if we got here, we failed to find the caller's context - return "" + return nil } func (entry Entry) HasCaller() (has bool) { return entry.Logger != nil && entry.Logger.ReportCaller && - entry.Caller != "" + entry.Caller != nil } // This function is not declared with a pointer value because otherwise diff --git a/json_formatter.go b/json_formatter.go index bdb569e2f..f5d45bbd7 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -82,7 +82,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() if entry.HasCaller() { - data[f.FieldMap.resolve(FieldKeyFunc)] = entry.Caller + data[f.FieldMap.resolve(FieldKeyFunc)] = entry.Caller.Function } var b *bytes.Buffer diff --git a/json_formatter_test.go b/json_formatter_test.go index 13fbf3195..695c36e54 100644 --- a/json_formatter_test.go +++ b/json_formatter_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "runtime" "strings" "testing" ) @@ -167,8 +168,8 @@ func TestFieldsInNestedDictionary(t *testing.T) { } logEntry := WithFields(Fields{ - "level": "level", - "test": "test", + "level": "level", + "test": "test", }) logEntry.Level = InfoLevel @@ -291,7 +292,7 @@ func TestFieldClashWithCaller(t *testing.T) { SetReportCaller(true) formatter := &JSONFormatter{} e := WithField("func", "howdy pardner") - e.Caller = "somefunc" + e.Caller = &runtime.Frame{Function: "somefunc"} b, err := formatter.Format(e) if err != nil { t.Fatal("Unable to format entry: ", err) diff --git a/text_formatter.go b/text_formatter.go index 409edc011..354387c15 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -208,7 +208,7 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin caller := "" if entry.HasCaller() { - caller = fmt.Sprintf(" %s()", entry.Caller) + caller = fmt.Sprintf(" %s()", entry.Caller.Function) } if f.DisableTimestamp { From fc3c0b14185cd8035d5766cc5d096e96cd119d49 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sun, 28 Oct 2018 14:07:57 +0100 Subject: [PATCH 415/547] move test functions and test utils functions in their own package --- hook_test.go | 5 ++- internal/testutils/testutils.go | 58 ++++++++++++++++++++++++ logrus_test.go | 79 +++++++-------------------------- 3 files changed, 79 insertions(+), 63 deletions(-) create mode 100644 internal/testutils/testutils.go diff --git a/hook_test.go b/hook_test.go index 7196e990e..b9675935b 100644 --- a/hook_test.go +++ b/hook_test.go @@ -1,4 +1,4 @@ -package logrus +package logrus_test import ( "bytes" @@ -8,6 +8,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + . "github.com/sirupsen/logrus" + . "github.com/sirupsen/logrus/internal/testutils" ) type TestHook struct { diff --git a/internal/testutils/testutils.go b/internal/testutils/testutils.go new file mode 100644 index 000000000..20bc3c3b6 --- /dev/null +++ b/internal/testutils/testutils.go @@ -0,0 +1,58 @@ +package testutils + +import ( + "bytes" + "encoding/json" + "strconv" + "strings" + "testing" + + . "github.com/sirupsen/logrus" + + "github.com/stretchr/testify/require" +) + +func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) { + var buffer bytes.Buffer + var fields Fields + + logger := New() + logger.Out = &buffer + logger.Formatter = new(JSONFormatter) + + log(logger) + + err := json.Unmarshal(buffer.Bytes(), &fields) + require.Nil(t, err) + + assertions(fields) +} + +func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) { + var buffer bytes.Buffer + + logger := New() + logger.Out = &buffer + logger.Formatter = &TextFormatter{ + DisableColors: true, + } + + log(logger) + + fields := make(map[string]string) + for _, kv := range strings.Split(buffer.String(), " ") { + if !strings.Contains(kv, "=") { + continue + } + kvArr := strings.Split(kv, "=") + key := strings.TrimSpace(kvArr[0]) + val := kvArr[1] + if kvArr[1][0] == '"' { + var err error + val, err = strconv.Unquote(val) + require.NoError(t, err) + } + fields[key] = val + } + assertions(fields) +} diff --git a/logrus_test.go b/logrus_test.go index 2fe3653b1..3b1d25633 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -1,62 +1,19 @@ -package logrus +package logrus_test import ( "bytes" "encoding/json" "io/ioutil" - "strconv" - "strings" "sync" "testing" "time" "github.com/stretchr/testify/assert" -) - -func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) { - var buffer bytes.Buffer - var fields Fields - - logger := New() - logger.Out = &buffer - logger.Formatter = new(JSONFormatter) - - log(logger) - - err := json.Unmarshal(buffer.Bytes(), &fields) - assert.Nil(t, err) - - assertions(fields) -} + "github.com/stretchr/testify/require" -func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) { - var buffer bytes.Buffer - - logger := New() - logger.Out = &buffer - logger.Formatter = &TextFormatter{ - DisableColors: true, - } - - log(logger) - - fields := make(map[string]string) - for _, kv := range strings.Split(buffer.String(), " ") { - if !strings.Contains(kv, "=") { - continue - } - kvArr := strings.Split(kv, "=") - key := strings.TrimSpace(kvArr[0]) - val := kvArr[1] - if kvArr[1][0] == '"' { - var err error - val, err = strconv.Unquote(val) - assert.NoError(t, err) - } - fields[key] = val - } - assertions(fields) -} + . "github.com/sirupsen/logrus" + . "github.com/sirupsen/logrus/internal/testutils" +) // TestReportCaller verifies that when ReportCaller is set, the 'func' field // is added, and when it is unset it is not set or modified @@ -78,7 +35,8 @@ func TestReportCallerWhenConfigured(t *testing.T) { }, func(fields Fields) { assert.Equal(t, "testWithCaller", fields["msg"]) assert.Equal(t, "info", fields["level"]) - assert.Equal(t, "testing.tRunner", fields["func"]) + assert.Equal(t, + "github.com/sirupsen/logrus_test.TestReportCallerWhenConfigured.func3", fields["func"]) }) } @@ -91,9 +49,6 @@ func logSomething(t *testing.T, message string) Fields { logger.Formatter = new(JSONFormatter) logger.ReportCaller = true - // override the filter to allow reporting of functions within the logrus package - logrusPackage = "bogusForTesting" - entry := logger.WithFields(Fields{ "foo": "bar", }) @@ -103,8 +58,6 @@ func logSomething(t *testing.T, message string) Fields { err := json.Unmarshal(buffer.Bytes(), &fields) assert.Nil(t, err) - // now clear the override so as not to mess with other usage - logrusPackage = "" return fields } @@ -114,7 +67,7 @@ func TestReportCallerHelperDirect(t *testing.T) { assert.Equal(t, "direct", fields["msg"]) assert.Equal(t, "info", fields["level"]) - assert.Regexp(t, "github.com/.*/logrus.logSomething", fields["func"]) + assert.Regexp(t, "github.com/.*/logrus_test.logSomething", fields["func"]) } // TestReportCallerHelperDirect - verify reference when logging from a function called via pointer @@ -124,7 +77,7 @@ func TestReportCallerHelperViaPointer(t *testing.T) { assert.Equal(t, "via pointer", fields["msg"]) assert.Equal(t, "info", fields["level"]) - assert.Regexp(t, "github.com/.*/logrus.logSomething", fields["func"]) + assert.Regexp(t, "github.com/.*/logrus_test.logSomething", fields["func"]) } func TestPrint(t *testing.T) { @@ -286,7 +239,7 @@ func TestWithTimeShouldOverrideTime(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.WithTime(now).Info("foobar") }, func(fields Fields) { - assert.Equal(t, fields["time"], now.Format(defaultTimestampFormat)) + assert.Equal(t, fields["time"], now.Format(time.RFC3339)) }) } @@ -296,7 +249,7 @@ func TestWithTimeShouldNotOverrideFields(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.WithField("herp", "derp").WithTime(now).Info("blah") }, func(fields Fields) { - assert.Equal(t, fields["time"], now.Format(defaultTimestampFormat)) + assert.Equal(t, fields["time"], now.Format(time.RFC3339)) assert.Equal(t, fields["herp"], "derp") }) } @@ -307,7 +260,7 @@ func TestWithFieldShouldNotOverrideTime(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.WithTime(now).WithField("herp", "derp").Info("blah") }, func(fields Fields) { - assert.Equal(t, fields["time"], now.Format(defaultTimestampFormat)) + assert.Equal(t, fields["time"], now.Format(time.RFC3339)) assert.Equal(t, fields["herp"], "derp") }) } @@ -385,11 +338,12 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) { llog.Info("looks delicious") err := json.Unmarshal(buffer.Bytes(), &fields) - assert.NoError(t, err, "should have decoded first message") + require.NoError(t, err, "should have decoded first message") assert.Equal(t, len(fields), 5, "should have msg/time/level/func/context fields") assert.Equal(t, "looks delicious", fields["msg"]) assert.Equal(t, "eating raw fish", fields["context"]) - assert.Equal(t, "testing.tRunner", fields["func"]) + assert.Equal(t, + "github.com/sirupsen/logrus_test.TestNestedLoggingReportsCorrectCaller", fields["func"]) buffer.Reset() @@ -415,7 +369,8 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) { assert.Equal(t, "Brown", fields["James"]) assert.Equal(t, "The hardest workin' man in show business", fields["msg"]) assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry") - assert.Equal(t, "testing.tRunner", fields["func"]) + assert.Equal(t, + "github.com/sirupsen/logrus_test.TestNestedLoggingReportsCorrectCaller", fields["func"]) logger.ReportCaller = false // return to default value } From f7239ff3b8e25c2d3e0b004382c95273978fb5df Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sun, 28 Oct 2018 17:39:39 +0100 Subject: [PATCH 416/547] add file and line number in output when report caller is enabled --- formatter.go | 5 +++++ json_formatter.go | 1 + logrus_test.go | 10 ++++++++-- text_formatter.go | 10 ++++++++-- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/formatter.go b/formatter.go index db378488c..408883773 100644 --- a/formatter.go +++ b/formatter.go @@ -10,6 +10,7 @@ const ( FieldKeyTime = "time" FieldKeyLogrusError = "logrus_error" FieldKeyFunc = "func" + FieldKeyFile = "file" ) // The Formatter interface is used to implement a custom Formatter. It takes an @@ -69,5 +70,9 @@ func prefixFieldClashes(data Fields, fieldMap FieldMap, reportCaller bool) { if l, ok := data[funcKey]; ok { data["fields."+funcKey] = l } + fileKey := fieldMap.resolve(FieldKeyFile) + if l, ok := data[fileKey]; ok { + data["fields."+fileKey] = l + } } } diff --git a/json_formatter.go b/json_formatter.go index f5d45bbd7..260575359 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -83,6 +83,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() if entry.HasCaller() { data[f.FieldMap.resolve(FieldKeyFunc)] = entry.Caller.Function + data[f.FieldMap.resolve(FieldKeyFile)] = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) } var b *bytes.Buffer diff --git a/logrus_test.go b/logrus_test.go index 3b1d25633..27cddaff1 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "io/ioutil" + "os" "sync" "testing" "time" @@ -339,11 +340,14 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) { err := json.Unmarshal(buffer.Bytes(), &fields) require.NoError(t, err, "should have decoded first message") - assert.Equal(t, len(fields), 5, "should have msg/time/level/func/context fields") + assert.Equal(t, 6, len(fields), "should have msg/time/level/func/context fields") assert.Equal(t, "looks delicious", fields["msg"]) assert.Equal(t, "eating raw fish", fields["context"]) assert.Equal(t, "github.com/sirupsen/logrus_test.TestNestedLoggingReportsCorrectCaller", fields["func"]) + cwd, err := os.Getwd() + require.NoError(t, err) + assert.Equal(t, cwd+"/logrus_test.go:339", fields["file"]) buffer.Reset() @@ -361,7 +365,7 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) { err = json.Unmarshal(buffer.Bytes(), &fields) assert.NoError(t, err, "should have decoded second message") - assert.Equal(t, 10, len(fields), "should have all builtin fields plus foo,bar,baz,...") + assert.Equal(t, 11, len(fields), "should have all builtin fields plus foo,bar,baz,...") assert.Equal(t, "Stubblefield", fields["Clyde"]) assert.Equal(t, "Starks", fields["Jab'o"]) assert.Equal(t, "https://www.youtube.com/watch?v=V5DTznu-9v0", fields["uri"]) @@ -371,6 +375,8 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) { assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry") assert.Equal(t, "github.com/sirupsen/logrus_test.TestNestedLoggingReportsCorrectCaller", fields["func"]) + require.NoError(t, err) + assert.Equal(t, cwd+"/logrus_test.go:364", fields["file"]) logger.ReportCaller = false // return to default value } diff --git a/text_formatter.go b/text_formatter.go index 354387c15..c5f9ca3c4 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -126,7 +126,8 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError)) } if entry.HasCaller() { - fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFunc)) + fixedKeys = append(fixedKeys, + f.FieldMap.resolve(FieldKeyFunc), f.FieldMap.resolve(FieldKeyFile)) } if !f.DisableSorting { @@ -172,6 +173,10 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { value = entry.Message case f.FieldMap.resolve(FieldKeyLogrusError): value = entry.err + case f.FieldMap.resolve(FieldKeyFunc): + value = entry.Caller.Function + case f.FieldMap.resolve(FieldKeyFile): + value = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) default: value = entry.Data[key] } @@ -208,7 +213,8 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin caller := "" if entry.HasCaller() { - caller = fmt.Sprintf(" %s()", entry.Caller.Function) + caller = fmt.Sprintf("%s:%d %s()", + entry.Caller.File, entry.Caller.Line, entry.Caller.Function) } if f.DisableTimestamp { From a4e4cd7e9275ce5038dcf68c901f1d88378fb2ff Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sun, 28 Oct 2018 18:12:32 +0100 Subject: [PATCH 417/547] make file name comparison os independant --- logrus_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/logrus_test.go b/logrus_test.go index 27cddaff1..7c99825d7 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "io/ioutil" "os" + "path/filepath" "sync" "testing" "time" @@ -347,7 +348,7 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) { "github.com/sirupsen/logrus_test.TestNestedLoggingReportsCorrectCaller", fields["func"]) cwd, err := os.Getwd() require.NoError(t, err) - assert.Equal(t, cwd+"/logrus_test.go:339", fields["file"]) + assert.Equal(t, filepath.ToSlash(cwd+"/logrus_test.go:340"), filepath.ToSlash(fields["file"].(string))) buffer.Reset() @@ -376,7 +377,7 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) { assert.Equal(t, "github.com/sirupsen/logrus_test.TestNestedLoggingReportsCorrectCaller", fields["func"]) require.NoError(t, err) - assert.Equal(t, cwd+"/logrus_test.go:364", fields["file"]) + assert.Equal(t, filepath.ToSlash(cwd+"/logrus_test.go:365"), filepath.ToSlash(fields["file"].(string))) logger.ReportCaller = false // return to default value } From f8ba03f52e65ce9ed80b2c4feae2b5df50b98524 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Wed, 31 Oct 2018 07:01:35 +0100 Subject: [PATCH 418/547] fix panic in text formatter The panic was caused to a nil pointer access when report caller was activated with output coloring disabled Fixes #852 --- logrus_test.go | 12 ++++++++++++ text_formatter.go | 14 +++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/logrus_test.go b/logrus_test.go index f6a0f44f1..b12d71c8c 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -702,3 +702,15 @@ func TestLogLevelEnabled(t *testing.T) { assert.Equal(t, true, log.IsLevelEnabled(DebugLevel)) assert.Equal(t, true, log.IsLevelEnabled(TraceLevel)) } + +func TestReportCallerOnTextFormatter(t *testing.T) { + l := New() + + l.Formatter.(*TextFormatter).ForceColors = true + l.Formatter.(*TextFormatter).DisableColors = false + l.WithFields(Fields{"func": "func", "file": "file"}).Info("test") + + l.Formatter.(*TextFormatter).ForceColors = false + l.Formatter.(*TextFormatter).DisableColors = true + l.WithFields(Fields{"func": "func", "file": "file"}).Info("test") +} diff --git a/text_formatter.go b/text_formatter.go index c5f9ca3c4..49ec92f17 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -164,18 +164,18 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { } else { for _, key := range fixedKeys { var value interface{} - switch key { - case f.FieldMap.resolve(FieldKeyTime): + switch { + case key == f.FieldMap.resolve(FieldKeyTime): value = entry.Time.Format(timestampFormat) - case f.FieldMap.resolve(FieldKeyLevel): + case key == f.FieldMap.resolve(FieldKeyLevel): value = entry.Level.String() - case f.FieldMap.resolve(FieldKeyMsg): + case key == f.FieldMap.resolve(FieldKeyMsg): value = entry.Message - case f.FieldMap.resolve(FieldKeyLogrusError): + case key == f.FieldMap.resolve(FieldKeyLogrusError): value = entry.err - case f.FieldMap.resolve(FieldKeyFunc): + case key == f.FieldMap.resolve(FieldKeyFunc) && entry.HasCaller(): value = entry.Caller.Function - case f.FieldMap.resolve(FieldKeyFile): + case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller(): value = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) default: value = entry.Data[key] From 249db8ecab3b15bd354ad74a0c8210ffec0be2e7 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Thu, 1 Nov 2018 08:39:56 +0100 Subject: [PATCH 419/547] v1.2.0 changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff0471869..cb85d9f9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 1.2.0 +This new release introduces: + * A new method `SetReportCaller` in the `Logger` to enable the file, line and calling function from which the trace has been issued + * A new trace level named `Trace` whose level is below `Debug` + * A configurable exit function to be called upon a Fatal trace + * The `Level` object now implements `encoding.TextUnmarshaler` interface + # 1.1.1 This is a bug fix release. * fix the build break on Solaris From a92e13e31db41d098cbdb2d87e95522afc21b729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C7=8Ei-Li=C3=A0ng=20=22Hal=22=20W=C3=A1ng?= Date: Mon, 5 Nov 2018 12:29:28 +0000 Subject: [PATCH 420/547] fix race condition caused by writing to entry.Data, using the same technique as JSONFormatter --- text_formatter.go | 21 ++++++++++++--------- text_formatter_test.go | 2 +- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index 49ec92f17..4342d7319 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -107,14 +107,17 @@ func (f *TextFormatter) isColored() bool { // Format renders a single log entry func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { - prefixFieldClashes(entry.Data, f.FieldMap, entry.HasCaller()) - - keys := make([]string, 0, len(entry.Data)) - for k := range entry.Data { + data := make(Fields) + for k, v := range entry.Data { + data[k] = v + } + prefixFieldClashes(data, f.FieldMap, entry.HasCaller()) + keys := make([]string, 0, len(data)) + for k := range data { keys = append(keys, k) } - fixedKeys := make([]string, 0, 4+len(entry.Data)) + fixedKeys := make([]string, 0, 4+len(data)) if !f.DisableTimestamp { fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime)) } @@ -160,7 +163,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { timestampFormat = defaultTimestampFormat } if f.isColored() { - f.printColored(b, entry, keys, timestampFormat) + f.printColored(b, entry, keys, nil, timestampFormat) } else { for _, key := range fixedKeys { var value interface{} @@ -178,7 +181,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller(): value = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) default: - value = entry.Data[key] + value = data[key] } f.appendKeyValue(b, key, value) } @@ -188,7 +191,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { return b.Bytes(), nil } -func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) { +func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields, timestampFormat string) { var levelColor int switch entry.Level { case DebugLevel, TraceLevel: @@ -225,7 +228,7 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message) } for _, k := range keys { - v := entry.Data[k] + v := data[k] fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k) f.appendValue(b, v) } diff --git a/text_formatter_test.go b/text_formatter_test.go index b0d3a916a..e07809d08 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -144,7 +144,7 @@ func TestDisableLevelTruncation(t *testing.T) { tf := &TextFormatter{DisableLevelTruncation: disabled} var b bytes.Buffer entry.Level = level - tf.printColored(&b, entry, keys, timestampFormat) + tf.printColored(&b, entry, keys, nil, timestampFormat) logLine := (&b).String() if disabled { expected := strings.ToUpper(level.String()) From 30bb27dd98879d5f3327c8afdcecb5ddc4226bdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C7=8Ei-Li=C3=A0ng=20=22Hal=22=20W=C3=A1ng?= Date: Tue, 6 Nov 2018 10:01:28 +0000 Subject: [PATCH 421/547] fix missing parameter --- text_formatter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text_formatter.go b/text_formatter.go index 4342d7319..17f000453 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -163,7 +163,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { timestampFormat = defaultTimestampFormat } if f.isColored() { - f.printColored(b, entry, keys, nil, timestampFormat) + f.printColored(b, entry, keys, data, timestampFormat) } else { for _, key := range fixedKeys { var value interface{} From 39b9ac88620099435ec2dccbc1b42ff711b05e80 Mon Sep 17 00:00:00 2001 From: xrstf Date: Fri, 9 Nov 2018 13:34:45 +0100 Subject: [PATCH 422/547] fix hook example --- example_hook_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example_hook_test.go b/example_hook_test.go index 15118d26b..dc0e69f19 100644 --- a/example_hook_test.go +++ b/example_hook_test.go @@ -16,7 +16,7 @@ func Example_hook() { log.Formatter = new(logrus.TextFormatter) // default log.Formatter.(*logrus.TextFormatter).DisableColors = true // remove colors log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true // remove timestamp from test output - if sl, err := slhooks.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, ""); err != nil { + if sl, err := slhooks.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, ""); err == nil { log.Hooks.Add(sl) } log.Out = os.Stdout From a21c34f2243e9b7e6d0653f932a4fe6b885a351e Mon Sep 17 00:00:00 2001 From: ceriath Date: Fri, 16 Nov 2018 15:17:01 +0100 Subject: [PATCH 423/547] Disable colored output on windows entirely MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the textformatter on windows outputs ``←[31mERRO←[0m[0000] test windows`` when coloring is not disabled explicitly. However, windows up to windows 8.1 does not support colored output on cmd entirely. Windows 10 added support for it, which is off by default and has to be enabled via registry or environment variable. Therefore i suggest removing colored output on windows entirely to make the output usable again. --- text_formatter.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text_formatter.go b/text_formatter.go index 49ec92f17..ed578d26a 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "os" + "runtime" "sort" "strings" "sync" @@ -102,7 +103,7 @@ func (f *TextFormatter) isColored() bool { } } - return isColored && !f.DisableColors + return isColored && !f.DisableColors && (runtime.GOOS != "windows") } // Format renders a single log entry From be34e5ac5430701c3d1227f3993475e7a55ca477 Mon Sep 17 00:00:00 2001 From: ceriath Date: Fri, 16 Nov 2018 15:19:37 +0100 Subject: [PATCH 424/547] ignore expected color on windows --- text_formatter_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/text_formatter_test.go b/text_formatter_test.go index b0d3a916a..8dfec5abf 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "os" + "runtime" "sort" "strings" "testing" @@ -443,7 +444,11 @@ func TestTextFormatterIsColored(t *testing.T) { os.Setenv("CLICOLOR_FORCE", val.clicolorForceVal) } res := tf.isColored() - assert.Equal(subT, val.expectedResult, res) + if runtime.GOOS == "windows" { + assert.Equal(subT, false, res) + } else { + assert.Equal(subT, val.expectedResult, res) + } }) } } From 7516337efb96d39332844f63bef7ef7087b9cace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Sun, 25 Nov 2018 23:15:12 +0100 Subject: [PATCH 425/547] Travis: fix checkout dir to help contributors run Travis on their fork Set the Travis-CI checkout path to allow contributors running Travis-CI on their fork to have Travis-CI launching using the canonical import path instead of the path of the fork. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 1f953bebd..a8f154515 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: go +go_import_path: github.com/sirupsen/logrus env: - GOMAXPROCS=4 GORACE=halt_on_error=1 matrix: From 8457e05b488c2593653e1eefbfe29c52aac8f769 Mon Sep 17 00:00:00 2001 From: Maxim Sukharev Date: Wed, 5 Dec 2018 19:14:19 +0100 Subject: [PATCH 426/547] Skip func pointer type value in fields Before there was introduced a fix for JSONFormatter when func type value passed as Field. This commit does the same but for pointer to func. Ref: https://github.com/sirupsen/logrus/issues/642 https://github.com/sirupsen/logrus/pull/832 --- entry.go | 19 ++++++++++++++----- entry_test.go | 13 +++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/entry.go b/entry.go index cc85d3aab..4c00ba5c3 100644 --- a/entry.go +++ b/entry.go @@ -108,18 +108,27 @@ func (entry *Entry) WithFields(fields Fields) *Entry { for k, v := range entry.Data { data[k] = v } - var field_err string + var fieldErr string for k, v := range fields { - if t := reflect.TypeOf(v); t != nil && t.Kind() == reflect.Func { - field_err = fmt.Sprintf("can not add field %q", k) + isErrField := false + if t := reflect.TypeOf(v); t != nil { + switch t.Kind() { + case reflect.Func: + isErrField = true + case reflect.Ptr: + isErrField = t.Elem().Kind() == reflect.Func + } + } + if isErrField { + fieldErr = fmt.Sprintf("can not add field %q", k) if entry.err != "" { - field_err = entry.err + ", " + field_err + fieldErr = entry.err + ", " + fieldErr } } else { data[k] = v } } - return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: field_err} + return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr} } // Overrides the time of the Entry. diff --git a/entry_test.go b/entry_test.go index a81e2b383..7afcc1159 100644 --- a/entry_test.go +++ b/entry_test.go @@ -113,3 +113,16 @@ func TestEntryHooksPanic(t *testing.T) { entry := NewEntry(logger) entry.Info(badMessage) } + +func TestEntryWithIncorrectField(t *testing.T) { + assert := assert.New(t) + + fn := func() {} + + e := Entry{} + eWithFunc := e.WithFields(Fields{"func": fn}) + eWithFuncPtr := e.WithFields(Fields{"funcPtr": &fn}) + + assert.Equal(eWithFunc.err, `can not add field "func"`) + assert.Equal(eWithFuncPtr.err, `can not add field "funcPtr"`) +} From 00f15144b80de3e45c29835081f04f94a968e516 Mon Sep 17 00:00:00 2001 From: Ceriath Date: Sun, 9 Dec 2018 21:47:44 +0100 Subject: [PATCH 427/547] respect ForceColor and environment variables over OS check --- text_formatter.go | 4 ++-- text_formatter_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index ed578d26a..acb0dc0e8 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -91,7 +91,7 @@ func (f *TextFormatter) init(entry *Entry) { } func (f *TextFormatter) isColored() bool { - isColored := f.ForceColors || f.isTerminal + isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows")) if f.EnvironmentOverrideColors { if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok && force != "0" { @@ -103,7 +103,7 @@ func (f *TextFormatter) isColored() bool { } } - return isColored && !f.DisableColors && (runtime.GOOS != "windows") + return isColored && !f.DisableColors } // Format renders a single log entry diff --git a/text_formatter_test.go b/text_formatter_test.go index 8dfec5abf..5b132373b 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -444,7 +444,7 @@ func TestTextFormatterIsColored(t *testing.T) { os.Setenv("CLICOLOR_FORCE", val.clicolorForceVal) } res := tf.isColored() - if runtime.GOOS == "windows" { + if runtime.GOOS == "windows" && !tf.ForceColors && !val.clicolorForceIsSet { assert.Equal(subT, false, res) } else { assert.Equal(subT, val.expectedResult, res) From ed7ca1fd8f127b787be7d339f5f442da00645e43 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Fri, 14 Dec 2018 17:01:34 +0100 Subject: [PATCH 428/547] do not clear error formatting informative field --- entry.go | 12 +++++++----- entry_test.go | 13 +++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/entry.go b/entry.go index 4c00ba5c3..419e2d1e4 100644 --- a/entry.go +++ b/entry.go @@ -108,7 +108,7 @@ func (entry *Entry) WithFields(fields Fields) *Entry { for k, v := range entry.Data { data[k] = v } - var fieldErr string + fieldErr := entry.err for k, v := range fields { isErrField := false if t := reflect.TypeOf(v); t != nil { @@ -120,9 +120,11 @@ func (entry *Entry) WithFields(fields Fields) *Entry { } } if isErrField { - fieldErr = fmt.Sprintf("can not add field %q", k) - if entry.err != "" { - fieldErr = entry.err + ", " + fieldErr + tmp := fmt.Sprintf("can not add field %q", k) + if fieldErr != "" { + fieldErr = entry.err + ", " + tmp + } else { + fieldErr = tmp } } else { data[k] = v @@ -133,7 +135,7 @@ func (entry *Entry) WithFields(fields Fields) *Entry { // Overrides the time of the Entry. func (entry *Entry) WithTime(t time.Time) *Entry { - return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t} + return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t, err: entry.err} } // getPackageName reduces a fully qualified function name to the package name diff --git a/entry_test.go b/entry_test.go index 7afcc1159..5e6634112 100644 --- a/entry_test.go +++ b/entry_test.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -125,4 +126,16 @@ func TestEntryWithIncorrectField(t *testing.T) { assert.Equal(eWithFunc.err, `can not add field "func"`) assert.Equal(eWithFuncPtr.err, `can not add field "funcPtr"`) + + eWithFunc = eWithFunc.WithField("not_a_func", "it is a string") + eWithFuncPtr = eWithFuncPtr.WithField("not_a_func", "it is a string") + + assert.Equal(eWithFunc.err, `can not add field "func"`) + assert.Equal(eWithFuncPtr.err, `can not add field "funcPtr"`) + + eWithFunc = eWithFunc.WithTime(time.Now()) + eWithFuncPtr = eWithFuncPtr.WithTime(time.Now()) + + assert.Equal(eWithFunc.err, `can not add field "func"`) + assert.Equal(eWithFuncPtr.err, `can not add field "funcPtr"`) } From 094f831e9b8830fc81e03423ad3eb37ec881cf17 Mon Sep 17 00:00:00 2001 From: Lisa Ugray Date: Thu, 15 Nov 2018 14:34:22 -0500 Subject: [PATCH 429/547] Add Generic Log functions with level via argument --- entry.go | 100 +++++++++++++---------------------- logger.go | 152 ++++++++++++++---------------------------------------- 2 files changed, 77 insertions(+), 175 deletions(-) diff --git a/entry.go b/entry.go index cc85d3aab..02247b30f 100644 --- a/entry.go +++ b/entry.go @@ -240,16 +240,18 @@ func (entry *Entry) write() { } } -func (entry *Entry) Trace(args ...interface{}) { - if entry.Logger.IsLevelEnabled(TraceLevel) { - entry.log(TraceLevel, fmt.Sprint(args...)) +func (entry *Entry) Log(level Level, args ...interface{}) { + if entry.Logger.IsLevelEnabled(level) { + entry.log(level, fmt.Sprint(args...)) } } +func (entry *Entry) Trace(args ...interface{}) { + entry.Log(TraceLevel, args...) +} + func (entry *Entry) Debug(args ...interface{}) { - if entry.Logger.IsLevelEnabled(DebugLevel) { - entry.log(DebugLevel, fmt.Sprint(args...)) - } + entry.Log(DebugLevel, args...) } func (entry *Entry) Print(args ...interface{}) { @@ -257,15 +259,11 @@ func (entry *Entry) Print(args ...interface{}) { } func (entry *Entry) Info(args ...interface{}) { - if entry.Logger.IsLevelEnabled(InfoLevel) { - entry.log(InfoLevel, fmt.Sprint(args...)) - } + entry.Log(InfoLevel, args...) } func (entry *Entry) Warn(args ...interface{}) { - if entry.Logger.IsLevelEnabled(WarnLevel) { - entry.log(WarnLevel, fmt.Sprint(args...)) - } + entry.Log(WarnLevel, args...) } func (entry *Entry) Warning(args ...interface{}) { @@ -273,43 +271,35 @@ func (entry *Entry) Warning(args ...interface{}) { } func (entry *Entry) Error(args ...interface{}) { - if entry.Logger.IsLevelEnabled(ErrorLevel) { - entry.log(ErrorLevel, fmt.Sprint(args...)) - } + entry.Log(ErrorLevel, args...) } func (entry *Entry) Fatal(args ...interface{}) { - if entry.Logger.IsLevelEnabled(FatalLevel) { - entry.log(FatalLevel, fmt.Sprint(args...)) - } + entry.Log(FatalLevel, args...) entry.Logger.Exit(1) } func (entry *Entry) Panic(args ...interface{}) { - if entry.Logger.IsLevelEnabled(PanicLevel) { - entry.log(PanicLevel, fmt.Sprint(args...)) - } + entry.Log(PanicLevel, args...) panic(fmt.Sprint(args...)) } // Entry Printf family functions +func (entry *Entry) Logf(level Level, format string, args ...interface{}) { + entry.Log(level, fmt.Sprintf(format, args...)) +} + func (entry *Entry) Tracef(format string, args ...interface{}) { - if entry.Logger.IsLevelEnabled(TraceLevel) { - entry.Trace(fmt.Sprintf(format, args...)) - } + entry.Logf(TraceLevel, format, args...) } func (entry *Entry) Debugf(format string, args ...interface{}) { - if entry.Logger.IsLevelEnabled(DebugLevel) { - entry.Debug(fmt.Sprintf(format, args...)) - } + entry.Logf(DebugLevel, format, args...) } func (entry *Entry) Infof(format string, args ...interface{}) { - if entry.Logger.IsLevelEnabled(InfoLevel) { - entry.Info(fmt.Sprintf(format, args...)) - } + entry.Logf(InfoLevel, format, args...) } func (entry *Entry) Printf(format string, args ...interface{}) { @@ -317,9 +307,7 @@ func (entry *Entry) Printf(format string, args ...interface{}) { } func (entry *Entry) Warnf(format string, args ...interface{}) { - if entry.Logger.IsLevelEnabled(WarnLevel) { - entry.Warn(fmt.Sprintf(format, args...)) - } + entry.Logf(WarnLevel, format, args...) } func (entry *Entry) Warningf(format string, args ...interface{}) { @@ -327,42 +315,36 @@ func (entry *Entry) Warningf(format string, args ...interface{}) { } func (entry *Entry) Errorf(format string, args ...interface{}) { - if entry.Logger.IsLevelEnabled(ErrorLevel) { - entry.Error(fmt.Sprintf(format, args...)) - } + entry.Logf(ErrorLevel, format, args...) } func (entry *Entry) Fatalf(format string, args ...interface{}) { - if entry.Logger.IsLevelEnabled(FatalLevel) { - entry.Fatal(fmt.Sprintf(format, args...)) - } + entry.Logf(FatalLevel, format, args...) entry.Logger.Exit(1) } func (entry *Entry) Panicf(format string, args ...interface{}) { - if entry.Logger.IsLevelEnabled(PanicLevel) { - entry.Panic(fmt.Sprintf(format, args...)) - } + entry.Logf(PanicLevel, format, args...) } // Entry Println family functions -func (entry *Entry) Traceln(args ...interface{}) { - if entry.Logger.IsLevelEnabled(TraceLevel) { - entry.Trace(entry.sprintlnn(args...)) +func (entry *Entry) Logln(level Level, args ...interface{}) { + if entry.Logger.IsLevelEnabled(level) { + entry.Log(level, entry.sprintlnn(args...)) } } +func (entry *Entry) Traceln(args ...interface{}) { + entry.Logln(TraceLevel, args...) +} + func (entry *Entry) Debugln(args ...interface{}) { - if entry.Logger.IsLevelEnabled(DebugLevel) { - entry.Debug(entry.sprintlnn(args...)) - } + entry.Logln(DebugLevel, args...) } func (entry *Entry) Infoln(args ...interface{}) { - if entry.Logger.IsLevelEnabled(InfoLevel) { - entry.Info(entry.sprintlnn(args...)) - } + entry.Logln(InfoLevel, args...) } func (entry *Entry) Println(args ...interface{}) { @@ -370,9 +352,7 @@ func (entry *Entry) Println(args ...interface{}) { } func (entry *Entry) Warnln(args ...interface{}) { - if entry.Logger.IsLevelEnabled(WarnLevel) { - entry.Warn(entry.sprintlnn(args...)) - } + entry.Logln(WarnLevel, args...) } func (entry *Entry) Warningln(args ...interface{}) { @@ -380,22 +360,16 @@ func (entry *Entry) Warningln(args ...interface{}) { } func (entry *Entry) Errorln(args ...interface{}) { - if entry.Logger.IsLevelEnabled(ErrorLevel) { - entry.Error(entry.sprintlnn(args...)) - } + entry.Logln(ErrorLevel, args...) } func (entry *Entry) Fatalln(args ...interface{}) { - if entry.Logger.IsLevelEnabled(FatalLevel) { - entry.Fatal(entry.sprintlnn(args...)) - } + entry.Logln(FatalLevel, args...) entry.Logger.Exit(1) } func (entry *Entry) Panicln(args ...interface{}) { - if entry.Logger.IsLevelEnabled(PanicLevel) { - entry.Panic(entry.sprintlnn(args...)) - } + entry.Logln(PanicLevel, args...) } // Sprintlnn => Sprint no newline. This is to get the behavior of how diff --git a/logger.go b/logger.go index 5ceca0eab..9bf64e22a 100644 --- a/logger.go +++ b/logger.go @@ -131,28 +131,24 @@ func (logger *Logger) WithTime(t time.Time) *Entry { return entry.WithTime(t) } -func (logger *Logger) Tracef(format string, args ...interface{}) { - if logger.IsLevelEnabled(TraceLevel) { +func (logger *Logger) Logf(level Level, format string, args ...interface{}) { + if logger.IsLevelEnabled(level) { entry := logger.newEntry() - entry.Tracef(format, args...) + entry.Logf(level, format, args...) logger.releaseEntry(entry) } } +func (logger *Logger) Tracef(format string, args ...interface{}) { + logger.Logf(TraceLevel, format, args...) +} + func (logger *Logger) Debugf(format string, args ...interface{}) { - if logger.IsLevelEnabled(DebugLevel) { - entry := logger.newEntry() - entry.Debugf(format, args...) - logger.releaseEntry(entry) - } + logger.Logf(DebugLevel, format, args...) } func (logger *Logger) Infof(format string, args ...interface{}) { - if logger.IsLevelEnabled(InfoLevel) { - entry := logger.newEntry() - entry.Infof(format, args...) - logger.releaseEntry(entry) - } + logger.Logf(InfoLevel, format, args...) } func (logger *Logger) Printf(format string, args ...interface{}) { @@ -162,68 +158,44 @@ func (logger *Logger) Printf(format string, args ...interface{}) { } func (logger *Logger) Warnf(format string, args ...interface{}) { - if logger.IsLevelEnabled(WarnLevel) { - entry := logger.newEntry() - entry.Warnf(format, args...) - logger.releaseEntry(entry) - } + logger.Logf(WarnLevel, format, args...) } func (logger *Logger) Warningf(format string, args ...interface{}) { - if logger.IsLevelEnabled(WarnLevel) { - entry := logger.newEntry() - entry.Warnf(format, args...) - logger.releaseEntry(entry) - } + logger.Warnf(format, args...) } func (logger *Logger) Errorf(format string, args ...interface{}) { - if logger.IsLevelEnabled(ErrorLevel) { - entry := logger.newEntry() - entry.Errorf(format, args...) - logger.releaseEntry(entry) - } + logger.Logf(ErrorLevel, format, args...) } func (logger *Logger) Fatalf(format string, args ...interface{}) { - if logger.IsLevelEnabled(FatalLevel) { - entry := logger.newEntry() - entry.Fatalf(format, args...) - logger.releaseEntry(entry) - } + logger.Logf(FatalLevel, format, args...) logger.Exit(1) } func (logger *Logger) Panicf(format string, args ...interface{}) { - if logger.IsLevelEnabled(PanicLevel) { + logger.Logf(PanicLevel, format, args...) +} + +func (logger *Logger) Log(level Level, args ...interface{}) { + if logger.IsLevelEnabled(level) { entry := logger.newEntry() - entry.Panicf(format, args...) + entry.Log(level, args...) logger.releaseEntry(entry) } } func (logger *Logger) Trace(args ...interface{}) { - if logger.IsLevelEnabled(TraceLevel) { - entry := logger.newEntry() - entry.Trace(args...) - logger.releaseEntry(entry) - } + logger.Log(TraceLevel, args...) } func (logger *Logger) Debug(args ...interface{}) { - if logger.IsLevelEnabled(DebugLevel) { - entry := logger.newEntry() - entry.Debug(args...) - logger.releaseEntry(entry) - } + logger.Log(DebugLevel, args...) } func (logger *Logger) Info(args ...interface{}) { - if logger.IsLevelEnabled(InfoLevel) { - entry := logger.newEntry() - entry.Info(args...) - logger.releaseEntry(entry) - } + logger.Log(InfoLevel, args...) } func (logger *Logger) Print(args ...interface{}) { @@ -233,68 +205,44 @@ func (logger *Logger) Print(args ...interface{}) { } func (logger *Logger) Warn(args ...interface{}) { - if logger.IsLevelEnabled(WarnLevel) { - entry := logger.newEntry() - entry.Warn(args...) - logger.releaseEntry(entry) - } + logger.Log(WarnLevel, args...) } func (logger *Logger) Warning(args ...interface{}) { - if logger.IsLevelEnabled(WarnLevel) { - entry := logger.newEntry() - entry.Warn(args...) - logger.releaseEntry(entry) - } + logger.Warn(args...) } func (logger *Logger) Error(args ...interface{}) { - if logger.IsLevelEnabled(ErrorLevel) { - entry := logger.newEntry() - entry.Error(args...) - logger.releaseEntry(entry) - } + logger.Log(ErrorLevel, args...) } func (logger *Logger) Fatal(args ...interface{}) { - if logger.IsLevelEnabled(FatalLevel) { - entry := logger.newEntry() - entry.Fatal(args...) - logger.releaseEntry(entry) - } + logger.Log(FatalLevel, args...) logger.Exit(1) } func (logger *Logger) Panic(args ...interface{}) { - if logger.IsLevelEnabled(PanicLevel) { + logger.Log(PanicLevel, args...) +} + +func (logger *Logger) Logln(level Level, args ...interface{}) { + if logger.IsLevelEnabled(level) { entry := logger.newEntry() - entry.Panic(args...) + entry.Logln(level, args...) logger.releaseEntry(entry) } } func (logger *Logger) Traceln(args ...interface{}) { - if logger.IsLevelEnabled(TraceLevel) { - entry := logger.newEntry() - entry.Traceln(args...) - logger.releaseEntry(entry) - } + logger.Logln(TraceLevel, args...) } func (logger *Logger) Debugln(args ...interface{}) { - if logger.IsLevelEnabled(DebugLevel) { - entry := logger.newEntry() - entry.Debugln(args...) - logger.releaseEntry(entry) - } + logger.Logln(DebugLevel, args...) } func (logger *Logger) Infoln(args ...interface{}) { - if logger.IsLevelEnabled(InfoLevel) { - entry := logger.newEntry() - entry.Infoln(args...) - logger.releaseEntry(entry) - } + logger.Logln(InfoLevel, args...) } func (logger *Logger) Println(args ...interface{}) { @@ -304,44 +252,24 @@ func (logger *Logger) Println(args ...interface{}) { } func (logger *Logger) Warnln(args ...interface{}) { - if logger.IsLevelEnabled(WarnLevel) { - entry := logger.newEntry() - entry.Warnln(args...) - logger.releaseEntry(entry) - } + logger.Logln(WarnLevel, args...) } func (logger *Logger) Warningln(args ...interface{}) { - if logger.IsLevelEnabled(WarnLevel) { - entry := logger.newEntry() - entry.Warnln(args...) - logger.releaseEntry(entry) - } + logger.Warn(args...) } func (logger *Logger) Errorln(args ...interface{}) { - if logger.IsLevelEnabled(ErrorLevel) { - entry := logger.newEntry() - entry.Errorln(args...) - logger.releaseEntry(entry) - } + logger.Logln(ErrorLevel, args...) } func (logger *Logger) Fatalln(args ...interface{}) { - if logger.IsLevelEnabled(FatalLevel) { - entry := logger.newEntry() - entry.Fatalln(args...) - logger.releaseEntry(entry) - } + logger.Logln(FatalLevel, args...) logger.Exit(1) } func (logger *Logger) Panicln(args ...interface{}) { - if logger.IsLevelEnabled(PanicLevel) { - entry := logger.newEntry() - entry.Panicln(args...) - logger.releaseEntry(entry) - } + logger.Logln(PanicLevel, args...) } func (logger *Logger) Exit(code int) { From 8e5a22282020a9658130fa16f1b284725a51fb1b Mon Sep 17 00:00:00 2001 From: David Bariod Date: Wed, 26 Dec 2018 17:43:14 +0100 Subject: [PATCH 430/547] Implement TextUnmarshaller interface for Level type Since the implementation of the TextMarshaller interface we could not unmarshal previously json marshalled Level value. Fixes #873 --- level_test.go | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ logrus.go | 42 ++++++++++++++++++++-------------- logrus_test.go | 13 ----------- 3 files changed, 87 insertions(+), 30 deletions(-) create mode 100644 level_test.go diff --git a/level_test.go b/level_test.go new file mode 100644 index 000000000..78915c4f2 --- /dev/null +++ b/level_test.go @@ -0,0 +1,62 @@ +package logrus_test + +import ( + "bytes" + "encoding/json" + "testing" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" +) + +func TestLevelJsonEncoding(t *testing.T) { + type X struct { + Level logrus.Level + } + + var x X + x.Level = logrus.WarnLevel + var buf bytes.Buffer + enc := json.NewEncoder(&buf) + require.NoError(t, enc.Encode(x)) + dec := json.NewDecoder(&buf) + var y X + require.NoError(t, dec.Decode(&y)) +} + +func TestLevelUnmarshalText(t *testing.T) { + var u logrus.Level + for _, level := range logrus.AllLevels { + t.Run(level.String(), func(t *testing.T) { + require.NoError(t, u.UnmarshalText([]byte(level.String()))) + require.Equal(t, level, u) + }) + } + t.Run("invalid", func(t *testing.T) { + require.Error(t, u.UnmarshalText([]byte("invalid"))) + }) +} + +func TestLevelMarshalText(t *testing.T) { + levelStrings := []string{ + "panic", + "fatal", + "error", + "warning", + "info", + "debug", + "trace", + } + for idx, val := range logrus.AllLevels { + level := val + t.Run(level.String(), func(t *testing.T) { + var cmp logrus.Level + b, err := level.MarshalText() + require.NoError(t, err) + require.Equal(t, levelStrings[idx], string(b)) + err = cmp.UnmarshalText(b) + require.NoError(t, err) + require.Equal(t, level, cmp) + }) + } +} diff --git a/logrus.go b/logrus.go index 4ef451866..c1ca88990 100644 --- a/logrus.go +++ b/logrus.go @@ -14,24 +14,11 @@ type Level uint32 // Convert the Level to a string. E.g. PanicLevel becomes "panic". func (level Level) String() string { - switch level { - case TraceLevel: - return "trace" - case DebugLevel: - return "debug" - case InfoLevel: - return "info" - case WarnLevel: - return "warning" - case ErrorLevel: - return "error" - case FatalLevel: - return "fatal" - case PanicLevel: - return "panic" + if b, err := level.MarshalText(); err == nil { + return string(b) + } else { + return "unknown" } - - return "unknown" } // ParseLevel takes a string level and returns the Logrus log level constant. @@ -69,6 +56,27 @@ func (level *Level) UnmarshalText(text []byte) error { return nil } +func (level Level) MarshalText() ([]byte, error) { + switch level { + case TraceLevel: + return []byte("trace"), nil + case DebugLevel: + return []byte("debug"), nil + case InfoLevel: + return []byte("info"), nil + case WarnLevel: + return []byte("warning"), nil + case ErrorLevel: + return []byte("error"), nil + case FatalLevel: + return []byte("fatal"), nil + case PanicLevel: + return []byte("panic"), nil + } + + return nil, fmt.Errorf("not a valid lorus level %q", level) +} + // A constant exposing all logging levels var AllLevels = []Level{ PanicLevel, diff --git a/logrus_test.go b/logrus_test.go index b12d71c8c..5c740fded 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -508,19 +508,6 @@ func TestParseLevel(t *testing.T) { assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error()) } -func TestUnmarshalText(t *testing.T) { - var u Level - for _, level := range AllLevels { - t.Run(level.String(), func(t *testing.T) { - assert.NoError(t, u.UnmarshalText([]byte(level.String()))) - assert.Equal(t, level, u) - }) - } - t.Run("invalid", func(t *testing.T) { - assert.Error(t, u.UnmarshalText([]byte("invalid"))) - }) -} - func TestGetSetLevelRace(t *testing.T) { wg := sync.WaitGroup{} for i := 0; i < 100; i++ { From 50f3b6798b523b5f39277d14d32f827d0dee7ebd Mon Sep 17 00:00:00 2001 From: Lisa Ugray Date: Wed, 2 Jan 2019 14:58:51 -0500 Subject: [PATCH 431/547] Remove sensitivity to file line changes --- logrus_test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/logrus_test.go b/logrus_test.go index b12d71c8c..afa41d22f 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -3,9 +3,11 @@ package logrus_test import ( "bytes" "encoding/json" + "fmt" "io/ioutil" "os" "path/filepath" + "runtime" "sync" "testing" "time" @@ -338,6 +340,7 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) { llog := logger.WithField("context", "eating raw fish") llog.Info("looks delicious") + _, _, line, _ := runtime.Caller(0) err := json.Unmarshal(buffer.Bytes(), &fields) require.NoError(t, err, "should have decoded first message") @@ -348,7 +351,7 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) { "github.com/sirupsen/logrus_test.TestNestedLoggingReportsCorrectCaller", fields["func"]) cwd, err := os.Getwd() require.NoError(t, err) - assert.Equal(t, filepath.ToSlash(cwd+"/logrus_test.go:340"), filepath.ToSlash(fields["file"].(string))) + assert.Equal(t, filepath.ToSlash(fmt.Sprintf("%s/logrus_test.go:%d", cwd, line-1)), filepath.ToSlash(fields["file"].(string))) buffer.Reset() @@ -363,6 +366,7 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) { }).WithFields(Fields{ "James": "Brown", }).Print("The hardest workin' man in show business") + _, _, line, _ = runtime.Caller(0) err = json.Unmarshal(buffer.Bytes(), &fields) assert.NoError(t, err, "should have decoded second message") @@ -377,7 +381,7 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) { assert.Equal(t, "github.com/sirupsen/logrus_test.TestNestedLoggingReportsCorrectCaller", fields["func"]) require.NoError(t, err) - assert.Equal(t, filepath.ToSlash(cwd+"/logrus_test.go:365"), filepath.ToSlash(fields["file"].(string))) + assert.Equal(t, filepath.ToSlash(fmt.Sprintf("%s/logrus_test.go:%d", cwd, line-1)), filepath.ToSlash(fields["file"].(string))) logger.ReportCaller = false // return to default value } From 99f47a00303da224b01cd30e1890d2dcab026455 Mon Sep 17 00:00:00 2001 From: Lisa Ugray Date: Wed, 2 Jan 2019 14:59:36 -0500 Subject: [PATCH 432/547] Test Log --- logrus_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/logrus_test.go b/logrus_test.go index afa41d22f..c1505ab40 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -111,6 +111,15 @@ func TestWarn(t *testing.T) { }) } +func TestLog(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Log(WarnLevel, "test") + }, func(fields Fields) { + assert.Equal(t, "test", fields["msg"]) + assert.Equal(t, "warning", fields["level"]) + }) +} + func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) { LogAndAssertJSON(t, func(log *Logger) { log.Infoln("test", "test") From cef4387a25c697d013c418e9df3e70e2a1b9d017 Mon Sep 17 00:00:00 2001 From: Rich Poirier Date: Fri, 4 Jan 2019 17:46:35 -0800 Subject: [PATCH 433/547] Update Changelog for 1.3.0 --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb85d9f9f..a7294193c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +# 1.3.0 +This new release introduces: + * Log, Logf, Logln functions for Logger and Entry that take a Level + +Fixes: + * Building prometheus node_exporter on AIX (#840) + * Race condition in TextFormatter (#468) + * Travis CI import path (#868) + * Remove coloured output on Windows (#862) + * Pointer to func as field in JSONFormatter (#870) + * Properly marshal Levels (#873) + # 1.2.0 This new release introduces: * A new method `SetReportCaller` in the `Logger` to enable the file, line and calling function from which the trace has been issued From 6df18b1806ebc64c10d769de2a0f385bc903dfde Mon Sep 17 00:00:00 2001 From: Sergey Romanov Date: Sat, 12 Jan 2019 00:22:21 +0500 Subject: [PATCH 434/547] Remove unused variables in TextFormatter --- text_formatter.go | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index fb21649c9..786bedfe7 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -12,18 +12,13 @@ import ( ) const ( - nocolor = 0 - red = 31 - green = 32 - yellow = 33 - blue = 36 - gray = 37 + red = 31 + yellow = 33 + blue = 36 + gray = 37 ) -var ( - baseTimestamp time.Time - emptyFieldMap FieldMap -) +var baseTimestamp time.Time func init() { baseTimestamp = time.Now() From b622c3a4f279ff9244b33bc50661ebd6472dbeea Mon Sep 17 00:00:00 2001 From: David Bariod Date: Thu, 17 Jan 2019 09:37:03 +0100 Subject: [PATCH 435/547] Add an example hook which adds default fields --- example_default_field_value_test.go | 30 +++++++++++++++++++++++++++++ example_global_hook_test.go | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 example_default_field_value_test.go diff --git a/example_default_field_value_test.go b/example_default_field_value_test.go new file mode 100644 index 000000000..a72ece44a --- /dev/null +++ b/example_default_field_value_test.go @@ -0,0 +1,30 @@ +package logrus_test + +import ( + "github.com/sirupsen/logrus" + "os" +) + +type DefaultFieldHook struct { + GetValue func() string +} + +func (h *DefaultFieldHook) Levels() []logrus.Level { + return logrus.AllLevels +} + +func (h *DefaultFieldHook) Fire(e *logrus.Entry) error { + e.Data["aDefaultField"] = h.GetValue() + return nil +} + +func ExampleDefaultField() { + l := logrus.New() + l.Out = os.Stdout + l.Formatter = &logrus.TextFormatter{DisableTimestamp: true, DisableColors: true} + + l.AddHook(&DefaultFieldHook{GetValue: func() string { return "with its default value" }}) + l.Info("first log") + // Output: + // level=info msg="first log" aDefaultField="with its default value" +} diff --git a/example_global_hook_test.go b/example_global_hook_test.go index c81e448c2..aaf2f4b1f 100644 --- a/example_global_hook_test.go +++ b/example_global_hook_test.go @@ -21,7 +21,7 @@ func (h *GlobalHook) Fire(e *logrus.Entry) error { return nil } -func Example() { +func ExampleGlobalVariableHook() { l := logrus.New() l.Out = os.Stdout l.Formatter = &logrus.TextFormatter{DisableTimestamp: true, DisableColors: true} From 1beb6e86e3aea81cccb803d5ce4cb8ca45777da3 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Thu, 17 Jan 2019 13:42:01 +0100 Subject: [PATCH 436/547] Add a unit test to ensure hook are called in their registration order --- hook_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/hook_test.go b/hook_test.go index b9675935b..28f193df9 100644 --- a/hook_test.go +++ b/hook_test.go @@ -190,3 +190,27 @@ func TestAddHookRace(t *testing.T) { // actually assert on the hook }) } + +type HookCallFunc struct { + F func() +} + +func (h *HookCallFunc) Levels() []Level { + return AllLevels +} + +func (h *HookCallFunc) Fire(e *Entry) error { + h.F() + return nil +} + +func TestHookFireOrder(t *testing.T) { + checkers := []string{} + h := LevelHooks{} + h.Add(&HookCallFunc{F: func() { checkers = append(checkers, "first hook") }}) + h.Add(&HookCallFunc{F: func() { checkers = append(checkers, "second hook") }}) + h.Add(&HookCallFunc{F: func() { checkers = append(checkers, "third hook") }}) + + h.Fire(InfoLevel, &Entry{}) + require.Equal(t, []string{"first hook", "second hook", "third hook"}, checkers) +} From 89f781c11358ee51e37a81b204e78b2770e7c630 Mon Sep 17 00:00:00 2001 From: Jiang Xin Date: Tue, 22 Jan 2019 20:08:19 +0800 Subject: [PATCH 437/547] logger: fix wrong callback method Fix wrong callback in `logger.go`, and add test cases: 1. `logger.Warningln` should call `logger.Warnln`, not `logger.Warn`. 2. It's ok for `logger.Print` to call `entry.Info`, but calling `entry.Print` is better. Signed-off-by: Jiang Xin --- logger.go | 4 ++-- logger_test.go | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/logger.go b/logger.go index 9bf64e22a..4b95ab1cf 100644 --- a/logger.go +++ b/logger.go @@ -200,7 +200,7 @@ func (logger *Logger) Info(args ...interface{}) { func (logger *Logger) Print(args ...interface{}) { entry := logger.newEntry() - entry.Info(args...) + entry.Print(args...) logger.releaseEntry(entry) } @@ -256,7 +256,7 @@ func (logger *Logger) Warnln(args ...interface{}) { } func (logger *Logger) Warningln(args ...interface{}) { - logger.Warn(args...) + logger.Warnln(args...) } func (logger *Logger) Errorln(args ...interface{}) { diff --git a/logger_test.go b/logger_test.go index 73ba45061..50433e60c 100644 --- a/logger_test.go +++ b/logger_test.go @@ -6,6 +6,7 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -40,3 +41,25 @@ func TestNoFieldValueError(t *testing.T) { _, ok := data[FieldKeyLogrusError] require.False(t, ok) } + +func TestWarninglnNotEqualToWarning(t *testing.T) { + buf := &bytes.Buffer{} + bufln := &bytes.Buffer{} + + formatter := new(TextFormatter) + formatter.DisableTimestamp = true + formatter.DisableLevelTruncation = true + + l := &Logger{ + Out: buf, + Formatter: formatter, + Hooks: make(LevelHooks), + Level: DebugLevel, + } + l.Warning("hello,", "world") + + l.SetOutput(bufln) + l.Warningln("hello,", "world") + + assert.NotEqual(t, buf.String(), bufln.String(), "Warning() and Wantingln() should not be equal") +} From 767ea6ec5665d76afb4b373f09620f3663006a14 Mon Sep 17 00:00:00 2001 From: Anton Fisher Date: Sat, 26 Jan 2019 14:16:28 -0800 Subject: [PATCH 438/547] Add nested-logrus-formatter to README.md Hi, I'd like to propose another one third-party formatter for logrus (https://github.com/antonfisher/nested-logrus-formatter). Thanks. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 398731055..a4796eb07 100644 --- a/README.md +++ b/README.md @@ -365,6 +365,7 @@ Third party logging formatters: * [`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̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. +* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure. You can define your formatter by implementing the `Formatter` interface, requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a From f5d9197e3c57aed42906861360e362ebc90ce811 Mon Sep 17 00:00:00 2001 From: Georgi Dimitrov Date: Tue, 29 Jan 2019 18:29:38 +0000 Subject: [PATCH 439/547] Add a DeferExitHandler function Useful for running exit handlers in the same order as defer statements --- alt_exit.go | 18 ++++++++++++--- alt_exit_test.go | 58 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/alt_exit.go b/alt_exit.go index 8af90637a..8fd189e1c 100644 --- a/alt_exit.go +++ b/alt_exit.go @@ -51,9 +51,9 @@ func Exit(code int) { os.Exit(code) } -// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke -// all handlers. The handlers will also be invoked when any Fatal log entry is -// made. +// RegisterExitHandler appends a Logrus Exit handler to the list of handlers, +// call logrus.Exit to invoke all handlers. The handlers will also be invoked when +// any Fatal log entry is made. // // This method is useful when a caller wishes to use logrus to log a fatal // message but also needs to gracefully shutdown. An example usecase could be @@ -62,3 +62,15 @@ func Exit(code int) { func RegisterExitHandler(handler func()) { handlers = append(handlers, handler) } + +// DeferExitHandler prepends a Logrus Exit handler to the list of handlers, +// call logrus.Exit to invoke all handlers. The handlers will also be invoked when +// any Fatal log entry is made. +// +// This method is useful when a caller wishes to use logrus to log a fatal +// message but also needs to gracefully shutdown. An example usecase could be +// closing database connections, or sending a alert that the application is +// closing. +func DeferExitHandler(handler func()) { + handlers = append([]func(){handler}, handlers...) +} diff --git a/alt_exit_test.go b/alt_exit_test.go index 0a2ff5650..54d503cb4 100644 --- a/alt_exit_test.go +++ b/alt_exit_test.go @@ -14,9 +14,61 @@ import ( func TestRegister(t *testing.T) { current := len(handlers) - RegisterExitHandler(func() {}) - if len(handlers) != current+1 { - t.Fatalf("expected %d handlers, got %d", current+1, len(handlers)) + + var results []string + + h1 := func() { results = append(results, "first") } + h2 := func() { results = append(results, "second") } + + RegisterExitHandler(h1) + RegisterExitHandler(h2) + + if len(handlers) != current+2 { + t.Fatalf("expected %d handlers, got %d", current+2, len(handlers)) + } + + runHandlers() + + if len(results) != 2 { + t.Fatalf("expected 2 handlers to be run, ran %d", len(results)) + } + + if results[0] != "first" { + t.Fatal("expected handler h1 to be run first, but it wasn't") + } + + if results[1] != "second" { + t.Fatal("expected handler h2 to be run second, but it wasn't") + } +} + +func TestDefer(t *testing.T) { + current := len(handlers) + + var results []string + + h1 := func() { results = append(results, "first") } + h2 := func() { results = append(results, "second") } + + DeferExitHandler(h1) + DeferExitHandler(h2) + + if len(handlers) != current+2 { + t.Fatalf("expected %d handlers, got %d", current+2, len(handlers)) + } + + runHandlers() + + if len(results) != 2 { + t.Fatalf("expected 2 handlers to be run, ran %d", len(results)) + } + + if results[0] != "second" { + t.Fatal("expected handler h2 to be run first, but it wasn't") + } + + if results[1] != "first" { + t.Fatal("expected handler h1 to be run second, but it wasn't") } } From a41784b6600c5078c73fb6ce79a8cab69f884f93 Mon Sep 17 00:00:00 2001 From: CodeLingo Bot Date: Tue, 5 Feb 2019 05:52:27 +0000 Subject: [PATCH 440/547] Fix error formatting based on best practices from Code Review Comments Signed-off-by: CodeLingo Bot --- json_formatter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json_formatter.go b/json_formatter.go index 260575359..44db09380 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -98,7 +98,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { encoder.SetIndent("", " ") } if err := encoder.Encode(data); err != nil { - return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) + return nil, fmt.Errorf("failed to marshal fields to JSON, %v", err) } return b.Bytes(), nil From 2eb00037589a96256d582927860fe7639183ff6b Mon Sep 17 00:00:00 2001 From: Gavin Cabbage Date: Wed, 6 Feb 2019 14:51:07 -0500 Subject: [PATCH 441/547] prevent string formatting in Entry.Logf when log level is not enabled --- entry.go | 6 ++++-- entry_test.go | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/entry.go b/entry.go index df6d188de..c5b489e97 100644 --- a/entry.go +++ b/entry.go @@ -298,7 +298,9 @@ func (entry *Entry) Panic(args ...interface{}) { // Entry Printf family functions func (entry *Entry) Logf(level Level, format string, args ...interface{}) { - entry.Log(level, fmt.Sprintf(format, args...)) + if entry.Logger.IsLevelEnabled(level) { + entry.Log(level, fmt.Sprintf(format, args...)) + } } func (entry *Entry) Tracef(format string, args ...interface{}) { @@ -390,4 +392,4 @@ func (entry *Entry) Panicln(args ...interface{}) { func (entry *Entry) sprintlnn(args ...interface{}) string { msg := fmt.Sprintln(args...) return msg[:len(msg)-1] -} +} \ No newline at end of file diff --git a/entry_test.go b/entry_test.go index 5e6634112..c4ad8c891 100644 --- a/entry_test.go +++ b/entry_test.go @@ -139,3 +139,17 @@ func TestEntryWithIncorrectField(t *testing.T) { assert.Equal(eWithFunc.err, `can not add field "func"`) assert.Equal(eWithFuncPtr.err, `can not add field "funcPtr"`) } + +func TestEntryLogfLevel(t *testing.T) { + logger := New() + buffer := &bytes.Buffer{} + logger.Out = buffer + logger.SetLevel(InfoLevel) + entry := NewEntry(logger) + + entry.Logf(DebugLevel, "%s", "debug") + assert.NotContains(t, buffer.String(), "debug", ) + + entry.Logf(WarnLevel, "%s", "warn") + assert.Contains(t, buffer.String(), "warn", ) +} \ No newline at end of file From 06ce2d93d943d7d7133467e57a110a1cda23a3c2 Mon Sep 17 00:00:00 2001 From: noushavandijk Date: Wed, 20 Feb 2019 18:22:53 +0100 Subject: [PATCH 442/547] Fix infinite recursion on unknown Level.String() Using `%q` in the error string causes `MarshalText` to recurse (by calling String()). --- logrus.go | 2 +- logrus_test.go | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/logrus.go b/logrus.go index c1ca88990..8644761f7 100644 --- a/logrus.go +++ b/logrus.go @@ -74,7 +74,7 @@ func (level Level) MarshalText() ([]byte, error) { return []byte("panic"), nil } - return nil, fmt.Errorf("not a valid lorus level %q", level) + return nil, fmt.Errorf("not a valid logrus level %d", level) } // A constant exposing all logging levels diff --git a/logrus_test.go b/logrus_test.go index d857262a9..f6fe8bf9a 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -521,6 +521,13 @@ func TestParseLevel(t *testing.T) { assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error()) } +func TestLevelString(t *testing.T) { + var loggerlevel Level + loggerlevel = 32000 + + _ = loggerlevel.String() +} + func TestGetSetLevelRace(t *testing.T) { wg := sync.WaitGroup{} for i := 0; i < 100; i++ { From 8ce13d2aec848003df8bf7b36c5bdb4a5111f6c8 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Wed, 27 Feb 2019 13:02:20 +0100 Subject: [PATCH 443/547] Add a CallerPrettyfier callback to the json formatter --- entry.go | 2 +- json_formatter.go | 21 +++++++++++++++++++-- logrus_test.go | 13 ++++++++++++- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/entry.go b/entry.go index c5b489e97..fd377286c 100644 --- a/entry.go +++ b/entry.go @@ -392,4 +392,4 @@ func (entry *Entry) Panicln(args ...interface{}) { func (entry *Entry) sprintlnn(args ...interface{}) string { msg := fmt.Sprintln(args...) return msg[:len(msg)-1] -} \ No newline at end of file +} diff --git a/json_formatter.go b/json_formatter.go index 260575359..c494d073d 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "fmt" + "runtime" ) type fieldKey string @@ -42,6 +43,12 @@ type JSONFormatter struct { // } FieldMap FieldMap + // CallerPrettyfier can be set by the user to modify the content + // of the function and file keys in the json data when ReportCaller is + // activated. If any of the returned value is the empty string the + // corresponding key will be removed from json fields. + CallerPrettyfier func(*runtime.Frame) (function string, file string) + // PrettyPrint will indent all json logs PrettyPrint bool } @@ -82,8 +89,18 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() if entry.HasCaller() { - data[f.FieldMap.resolve(FieldKeyFunc)] = entry.Caller.Function - data[f.FieldMap.resolve(FieldKeyFile)] = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) + funcVal := entry.Caller.Function + fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) + if f.CallerPrettyfier != nil { + funcVal, fileVal = f.CallerPrettyfier(entry.Caller) + fmt.Println(funcVal, fileVal) + } + if funcVal != "" { + data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal + } + if fileVal != "" { + data[f.FieldMap.resolve(FieldKeyFile)] = fileVal + } } var b *bytes.Buffer diff --git a/logrus_test.go b/logrus_test.go index f6fe8bf9a..47a386f95 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -40,7 +40,18 @@ func TestReportCallerWhenConfigured(t *testing.T) { assert.Equal(t, "testWithCaller", fields["msg"]) assert.Equal(t, "info", fields["level"]) assert.Equal(t, - "github.com/sirupsen/logrus_test.TestReportCallerWhenConfigured.func3", fields["func"]) + "github.com/sirupsen/logrus_test.TestReportCallerWhenConfigured.func3", fields[FieldKeyFunc]) + }) + + LogAndAssertJSON(t, func(log *Logger) { + log.ReportCaller = true + log.Formatter.(*JSONFormatter).CallerPrettyfier = func(f *runtime.Frame) (string, string) { + return "somekindoffunc", "thisisafilename" + } + log.Print("testWithCallerPrettyfier") + }, func(fields Fields) { + assert.Equal(t, "somekindoffunc", fields[FieldKeyFunc]) + assert.Equal(t, "thisisafilename", fields[FieldKeyFile]) }) } From 9611364b362b93fb6a535b329263174e7efe27e5 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Wed, 27 Feb 2019 13:34:02 +0100 Subject: [PATCH 444/547] Add a CallerPrettyfier callback to the text formatter --- internal/testutils/testutils.go | 2 +- logrus_test.go | 11 +++++++++++ text_formatter.go | 28 ++++++++++++++++++++++++---- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/internal/testutils/testutils.go b/internal/testutils/testutils.go index 20bc3c3b6..6e3a6203e 100644 --- a/internal/testutils/testutils.go +++ b/internal/testutils/testutils.go @@ -40,7 +40,7 @@ func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields ma log(logger) fields := make(map[string]string) - for _, kv := range strings.Split(buffer.String(), " ") { + for _, kv := range strings.Split(strings.TrimRight(buffer.String(), "\n"), " ") { if !strings.Contains(kv, "=") { continue } diff --git a/logrus_test.go b/logrus_test.go index 47a386f95..dfa053bbd 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -53,6 +53,17 @@ func TestReportCallerWhenConfigured(t *testing.T) { assert.Equal(t, "somekindoffunc", fields[FieldKeyFunc]) assert.Equal(t, "thisisafilename", fields[FieldKeyFile]) }) + + LogAndAssertText(t, func(log *Logger) { + log.ReportCaller = true + log.Formatter.(*TextFormatter).CallerPrettyfier = func(f *runtime.Frame) (string, string) { + return "somekindoffunc", "thisisafilename" + } + log.Print("testWithCallerPrettyfier") + }, func(fields map[string]string) { + assert.Equal(t, "somekindoffunc", fields[FieldKeyFunc]) + assert.Equal(t, "thisisafilename", fields[FieldKeyFile]) + }) } func logSomething(t *testing.T, message string) Fields { diff --git a/text_formatter.go b/text_formatter.go index 786bedfe7..b3b1a30ca 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -72,6 +72,12 @@ type TextFormatter struct { // FieldKeyMsg: "@message"}} FieldMap FieldMap + // CallerPrettyfier can be set by the user to modify the content + // of the function and file keys in the json data when ReportCaller is + // activated. If any of the returned value is the empty string the + // corresponding key will be removed from json fields. + CallerPrettyfier func(*runtime.Frame) (function string, file string) + terminalInitOnce sync.Once } @@ -113,6 +119,8 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { keys = append(keys, k) } + var funcVal, fileVal string + fixedKeys := make([]string, 0, 4+len(data)) if !f.DisableTimestamp { fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime)) @@ -127,6 +135,12 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { if entry.HasCaller() { fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFunc), f.FieldMap.resolve(FieldKeyFile)) + if f.CallerPrettyfier != nil { + funcVal, fileVal = f.CallerPrettyfier(entry.Caller) + } else { + funcVal = entry.Caller.Function + fileVal = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) + } } if !f.DisableSorting { @@ -161,6 +175,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { if f.isColored() { f.printColored(b, entry, keys, data, timestampFormat) } else { + for _, key := range fixedKeys { var value interface{} switch { @@ -173,9 +188,9 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { case key == f.FieldMap.resolve(FieldKeyLogrusError): value = entry.err case key == f.FieldMap.resolve(FieldKeyFunc) && entry.HasCaller(): - value = entry.Caller.Function + value = funcVal case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller(): - value = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) + value = fileVal default: value = data[key] } @@ -212,8 +227,13 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin caller := "" if entry.HasCaller() { - caller = fmt.Sprintf("%s:%d %s()", - entry.Caller.File, entry.Caller.Line, entry.Caller.Function) + funcVal := fmt.Sprintf("%s()", entry.Caller.Function) + fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) + + if f.CallerPrettyfier != nil { + funcVal, fileVal = f.CallerPrettyfier(entry.Caller) + } + caller = fileVal + " " + funcVal } if f.DisableTimestamp { From 9571061c3d05c5a50b57c78dd8c51e63b3d9682d Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sun, 3 Mar 2019 11:52:04 +0100 Subject: [PATCH 445/547] Remove debug trace --- json_formatter.go | 1 - 1 file changed, 1 deletion(-) diff --git a/json_formatter.go b/json_formatter.go index c494d073d..ae95040ce 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -93,7 +93,6 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) if f.CallerPrettyfier != nil { funcVal, fileVal = f.CallerPrettyfier(entry.Caller) - fmt.Println(funcVal, fileVal) } if funcVal != "" { data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal From f47d63d2235f6c22895920df712488007e5036a8 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sun, 3 Mar 2019 11:53:51 +0100 Subject: [PATCH 446/547] Add and example for CallerPrettyfier --- example_custom_caller_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 example_custom_caller_test.go diff --git a/example_custom_caller_test.go b/example_custom_caller_test.go new file mode 100644 index 000000000..e0023b9ad --- /dev/null +++ b/example_custom_caller_test.go @@ -0,0 +1,28 @@ +package logrus_test + +import ( + "os" + "path" + "runtime" + "strings" + + "github.com/sirupsen/logrus" +) + +func ExampleCustomFormatter() { + l := logrus.New() + l.SetReportCaller(true) + l.Out = os.Stdout + l.Formatter = &logrus.JSONFormatter{ + DisableTimestamp: true, + CallerPrettyfier: func(f *runtime.Frame) (string, string) { + s := strings.Split(f.Function, ".") + funcname := s[len(s)-1] + _, filename := path.Split(f.File) + return funcname, filename + }, + } + l.Info("example of custom format caller") + // Output: + // {"file":"example_custom_caller_test.go","func":"ExampleCustomFormatter","level":"info","msg":"example of custom format caller"} +} From fcbfb263342b7d5e794bb1e564050a752060235d Mon Sep 17 00:00:00 2001 From: georlav Date: Mon, 4 Mar 2019 20:38:10 +0200 Subject: [PATCH 447/547] fix ReportCaller race condition --- entry.go | 2 ++ logrus_test.go | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/entry.go b/entry.go index fd377286c..4ca6081d3 100644 --- a/entry.go +++ b/entry.go @@ -206,7 +206,9 @@ func (entry Entry) log(level Level, msg string) { entry.Level = level entry.Message = msg if entry.Logger.ReportCaller { + entry.Logger.mu.Lock() entry.Caller = getCaller() + entry.Logger.mu.Unlock() } entry.fireHooks() diff --git a/logrus_test.go b/logrus_test.go index dfa053bbd..72b6ea254 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -743,3 +743,20 @@ func TestReportCallerOnTextFormatter(t *testing.T) { l.Formatter.(*TextFormatter).DisableColors = true l.WithFields(Fields{"func": "func", "file": "file"}).Info("test") } + +func TestSetReportCallerRace(t *testing.T) { + l := New() + l.Out = ioutil.Discard + l.SetReportCaller(true) + + var wg sync.WaitGroup + wg.Add(100) + + for i := 0; i < 100; i++ { + go func() { + l.Error("Some Error") + wg.Done() + }() + } + wg.Wait() +} From 6983a03f29c367ca92d379a83d0be0ee11115d84 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Wed, 6 Mar 2019 14:08:02 +0100 Subject: [PATCH 448/547] fix sync.Once usage instead of adding a mutex lock --- entry.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/entry.go b/entry.go index 4ca6081d3..191621487 100644 --- a/entry.go +++ b/entry.go @@ -156,20 +156,23 @@ func getPackageName(f string) string { // getCaller retrieves the name of the first non-logrus calling function func getCaller() *runtime.Frame { - // Restrict the lookback frames to avoid runaway lookups - pcs := make([]uintptr, maximumCallerDepth) - depth := runtime.Callers(minimumCallerDepth, pcs) - frames := runtime.CallersFrames(pcs[:depth]) // cache this package's fully-qualified name callerInitOnce.Do(func() { - logrusPackage = getPackageName(runtime.FuncForPC(pcs[0]).Name()) + pcs := make([]uintptr, 2) + _ = runtime.Callers(0, pcs) + logrusPackage = getPackageName(runtime.FuncForPC(pcs[1]).Name()) // now that we have the cache, we can skip a minimum count of known-logrus functions - // XXX this is dubious, the number of frames may vary store an entry in a logger interface + // XXX this is dubious, the number of frames may vary minimumCallerDepth = knownLogrusFrames }) + // Restrict the lookback frames to avoid runaway lookups + pcs := make([]uintptr, maximumCallerDepth) + depth := runtime.Callers(minimumCallerDepth, pcs) + frames := runtime.CallersFrames(pcs[:depth]) + for f, again := frames.Next(); again; f, again = frames.Next() { pkg := getPackageName(f.Function) @@ -206,9 +209,7 @@ func (entry Entry) log(level Level, msg string) { entry.Level = level entry.Message = msg if entry.Logger.ReportCaller { - entry.Logger.mu.Lock() entry.Caller = getCaller() - entry.Logger.mu.Unlock() } entry.fireHooks() From e33f61aab722fd74658730a0a6709b9e3bcfdbe4 Mon Sep 17 00:00:00 2001 From: Adam Renberg Tamm Date: Mon, 11 Mar 2019 11:47:03 +0100 Subject: [PATCH 449/547] Add WithContext --- entry.go | 14 ++++++++++++-- entry_test.go | 14 ++++++++++++++ exported.go | 6 ++++++ logger.go | 8 ++++++++ 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/entry.go b/entry.go index 191621487..3d2c9e0fb 100644 --- a/entry.go +++ b/entry.go @@ -2,6 +2,7 @@ package logrus import ( "bytes" + "context" "fmt" "os" "reflect" @@ -69,6 +70,9 @@ type Entry struct { // When formatter is called in entry.log(), a Buffer may be set to entry Buffer *bytes.Buffer + // Contains the context set by the user. Useful for hook processing etc. + Context context.Context + // err may contain a field formatting error err string } @@ -97,6 +101,12 @@ func (entry *Entry) WithError(err error) *Entry { return entry.WithField(ErrorKey, err) } +// Add a context to the Entry. +func (entry *Entry) WithContext(ctx context.Context) *Entry { + entry.Context = ctx + return entry +} + // Add a single field to the Entry. func (entry *Entry) WithField(key string, value interface{}) *Entry { return entry.WithFields(Fields{key: value}) @@ -130,12 +140,12 @@ func (entry *Entry) WithFields(fields Fields) *Entry { data[k] = v } } - return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr} + return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context} } // Overrides the time of the Entry. func (entry *Entry) WithTime(t time.Time) *Entry { - return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t, err: entry.err} + return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t, err: entry.err, Context: entry.Context} } // getPackageName reduces a fully qualified function name to the package name diff --git a/entry_test.go b/entry_test.go index c4ad8c891..f764085ef 100644 --- a/entry_test.go +++ b/entry_test.go @@ -2,6 +2,7 @@ package logrus import ( "bytes" + "context" "fmt" "testing" "time" @@ -33,6 +34,19 @@ func TestEntryWithError(t *testing.T) { } +func TestEntryWithContext(t *testing.T) { + assert := assert.New(t) + ctx := context.WithValue(context.Background(), "foo", "bar") + + assert.Equal(ctx, WithContext(ctx).Context) + + logger := New() + logger.Out = &bytes.Buffer{} + entry := NewEntry(logger) + + assert.Equal(ctx, entry.WithContext(ctx).Context) +} + func TestEntryPanicln(t *testing.T) { errBoom := fmt.Errorf("boom time") diff --git a/exported.go b/exported.go index 7342613c3..62fc2f219 100644 --- a/exported.go +++ b/exported.go @@ -1,6 +1,7 @@ package logrus import ( + "context" "io" "time" ) @@ -55,6 +56,11 @@ func WithError(err error) *Entry { return std.WithField(ErrorKey, err) } +// WithContext creates an entry from the standard logger and adds a context to it. +func WithContext(ctx context.Context) *Entry { + return std.WithContext(ctx) +} + // WithField creates an entry from the standard logger and adds a field to // it. If you want multiple fields, use `WithFields`. // diff --git a/logger.go b/logger.go index 4b95ab1cf..c0c0b1e55 100644 --- a/logger.go +++ b/logger.go @@ -1,6 +1,7 @@ package logrus import ( + "context" "io" "os" "sync" @@ -124,6 +125,13 @@ func (logger *Logger) WithError(err error) *Entry { return entry.WithError(err) } +// Add a context to the log entry. +func (logger *Logger) WithContext(ctx context.Context) *Entry { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + return entry.WithContext(ctx) +} + // Overrides the time of the log entry. func (logger *Logger) WithTime(t time.Time) *Entry { entry := logger.newEntry() From beac72f99e6fd28805b74f74eec051a6c26dfdb6 Mon Sep 17 00:00:00 2001 From: Adam Renberg Tamm Date: Mon, 11 Mar 2019 16:26:46 +0100 Subject: [PATCH 450/547] Add CHANGELOG for v1.4.0 --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7294193c..9978b415a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +# 1.4.0 +This new release introduces: + * Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848). + * Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter (#909, #911) + * Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919). + +Fixes: + * Fix wrong method calls `Logger.Print` and `Logger.Warningln` (#893). + * Update `Entry.Logf` to not do string formatting unless the log level is enabled (#903) + * Fix infinite recursion on unknown `Level.String()` (#907) + * Fix race condition in `getCaller` (#916). + + # 1.3.0 This new release introduces: * Log, Logf, Logln functions for Logger and Entry that take a Level From 9894569bb64d66e18d15a87dddf0cd23e3462caa Mon Sep 17 00:00:00 2001 From: Kirill Motkov Date: Wed, 13 Mar 2019 12:38:09 +0300 Subject: [PATCH 451/547] Rewrite if-else-if-else to switch statements --- text_formatter.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index b3b1a30ca..967de1aa2 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -95,11 +95,10 @@ func (f *TextFormatter) isColored() bool { isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows")) if f.EnvironmentOverrideColors { - if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok && force != "0" { + switch force, ok := os.LookupEnv("CLICOLOR_FORCE"); { + case ok && force != "0": isColored = true - } else if ok && force == "0" { - isColored = false - } else if os.Getenv("CLICOLOR") == "0" { + case ok && force == "0", os.Getenv("CLICOLOR") == "0": isColored = false } } @@ -236,11 +235,12 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin caller = fileVal + " " + funcVal } - if f.DisableTimestamp { + switch { + case f.DisableTimestamp: fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, entry.Message) - } else if !f.FullTimestamp { + case !f.FullTimestamp: fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message) - } else { + default: fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message) } for _, k := range keys { From d7449938e4b151a5274efc6b5f859fedca927d5e Mon Sep 17 00:00:00 2001 From: tbunyk Date: Fri, 15 Mar 2019 13:09:11 +0200 Subject: [PATCH 452/547] Add hook to send logs to custom writer #678 --- hooks/writer/README.md | 43 +++++++++++++++++++++++++++++++++++++ hooks/writer/writer.go | 29 +++++++++++++++++++++++++ hooks/writer/writer_test.go | 37 +++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 hooks/writer/README.md create mode 100644 hooks/writer/writer.go create mode 100644 hooks/writer/writer_test.go diff --git a/hooks/writer/README.md b/hooks/writer/README.md new file mode 100644 index 000000000..69676309f --- /dev/null +++ b/hooks/writer/README.md @@ -0,0 +1,43 @@ +# Writer Hooks for Logrus + +Send logs of given levels to any object with `io.Writer` interface. + +## Usage + +If you want for example send high level logs to `Stderr` and +logs of normal execution to `Stdout`, you could do it like this: + +```go +package main + +import ( + "io/ioutil" + "os" + + log "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus/hooks/writer" +) + +func main() { + log.SetOutput(ioutil.Discard) // Send all logs to nowhere by default + + log.AddHook(&writer.Hook{ // Send logs with level higher than warning to stderr + Writer: os.Stderr, + LogLevels: []log.Level{ + log.PanicLevel, + log.FatalLevel, + log.ErrorLevel, + log.WarnLevel, + }, + }) + log.AddHook(&writer.Hook{ // Send info and debug logs to stdout + Writer: os.Stdout, + LogLevels: []log.Level{ + log.InfoLevel, + log.DebugLevel, + }, + }) + log.Info("This will go to stdout") + log.Warn("This will go to stderr") +} +``` diff --git a/hooks/writer/writer.go b/hooks/writer/writer.go new file mode 100644 index 000000000..0532dee26 --- /dev/null +++ b/hooks/writer/writer.go @@ -0,0 +1,29 @@ +package writer + +import ( + "io" + + log "github.com/sirupsen/logrus" +) + +// Hook is a hook that writes logs of specified LogLevels to specified Writer +type Hook struct { + Writer io.Writer + LogLevels []log.Level +} + +// Fire will be called when some logging function is called with current hook +// It will format log entry to string and write it to appropriate writer +func (hook *Hook) Fire(entry *log.Entry) error { + line, err := entry.String() + if err != nil { + return err + } + _, err = hook.Writer.Write([]byte(line)) + return err +} + +// Levels define on which log levels this hook would trigger +func (hook *Hook) Levels() []log.Level { + return hook.LogLevels +} diff --git a/hooks/writer/writer_test.go b/hooks/writer/writer_test.go new file mode 100644 index 000000000..22cfdff47 --- /dev/null +++ b/hooks/writer/writer_test.go @@ -0,0 +1,37 @@ +package writer + +import ( + "bytes" + "io/ioutil" + "testing" + + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +func TestDifferentLevelsGoToDifferentWriters(t *testing.T) { + var a, b bytes.Buffer + + log.SetFormatter(&log.TextFormatter{ + DisableTimestamp: true, + }) + log.SetOutput(ioutil.Discard) // Send all logs to nowhere by default + + log.AddHook(&Hook{ + Writer: &a, + LogLevels: []log.Level{ + log.WarnLevel, + }, + }) + log.AddHook(&Hook{ // Send info and debug logs to stdout + Writer: &b, + LogLevels: []log.Level{ + log.InfoLevel, + }, + }) + log.Warn("send to a") + log.Info("send to b") + + assert.Equal(t, a.String(), "level=warning msg=\"send to a\"\n") + assert.Equal(t, b.String(), "level=info msg=\"send to b\"\n") +} From d0ea271c5116e2095e07f5de51cdd2f26abcd06c Mon Sep 17 00:00:00 2001 From: tbunyk Date: Fri, 15 Mar 2019 13:19:10 +0200 Subject: [PATCH 453/547] Fix some test conditions --- hooks/writer/writer_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/hooks/writer/writer_test.go b/hooks/writer/writer_test.go index 22cfdff47..a30d3b01a 100644 --- a/hooks/writer/writer_test.go +++ b/hooks/writer/writer_test.go @@ -14,6 +14,7 @@ func TestDifferentLevelsGoToDifferentWriters(t *testing.T) { log.SetFormatter(&log.TextFormatter{ DisableTimestamp: true, + DisableColors: true, }) log.SetOutput(ioutil.Discard) // Send all logs to nowhere by default From 8a6b2426ac20cd15a71949c2a6b04e9269a0eda8 Mon Sep 17 00:00:00 2001 From: tbunyk Date: Mon, 18 Mar 2019 16:07:31 +0200 Subject: [PATCH 454/547] Add Bytes() method to Entry, and use it to avoid double type cast --- entry.go | 7 ++++++- hooks/writer/writer.go | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/entry.go b/entry.go index 3d2c9e0fb..7e8ad25c1 100644 --- a/entry.go +++ b/entry.go @@ -85,10 +85,15 @@ func NewEntry(logger *Logger) *Entry { } } +// Returns the bytes representation of this entry from the formatter. +func (entry *Entry) Bytes() ([]byte, error) { + return entry.Logger.Formatter.Format(entry) +} + // Returns the string representation from the reader and ultimately the // formatter. func (entry *Entry) String() (string, error) { - serialized, err := entry.Logger.Formatter.Format(entry) + serialized, err := entry.Bytes() if err != nil { return "", err } diff --git a/hooks/writer/writer.go b/hooks/writer/writer.go index 0532dee26..1160c790e 100644 --- a/hooks/writer/writer.go +++ b/hooks/writer/writer.go @@ -15,11 +15,11 @@ type Hook struct { // Fire will be called when some logging function is called with current hook // It will format log entry to string and write it to appropriate writer func (hook *Hook) Fire(entry *log.Entry) error { - line, err := entry.String() + line, err := entry.Bytes() if err != nil { return err } - _, err = hook.Writer.Write([]byte(line)) + _, err = hook.Writer.Write(line) return err } From 7364c36022ceaadad0428f5717dec04c7bf51cef Mon Sep 17 00:00:00 2001 From: Emil Hessman Date: Sun, 24 Mar 2019 16:01:10 +0100 Subject: [PATCH 455/547] Add Go 1.12 to Travis CI build matrix --- .travis.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/.travis.yml b/.travis.yml index a8f154515..ff85ac772 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,21 @@ matrix: - go get golang.org/x/sys/windows script: - go test -race -v ./... + - go: 1.12.x + env: GO111MODULE=on + install: + - go mod download + script: + - go test -race -v ./... + - go: 1.12.x + env: GO111MODULE=off + install: + - go get github.com/stretchr/testify/assert + - go get golang.org/x/crypto/ssh/terminal + - go get golang.org/x/sys/unix + - go get golang.org/x/sys/windows + script: + - go test -race -v ./... - go: 1.10.x install: - go get github.com/stretchr/testify/assert @@ -50,3 +65,18 @@ matrix: - go get golang.org/x/sys/windows script: - go test -race -v -tags appengine ./... + - go: 1.12.x + env: GO111MODULE=on + install: + - go mod download + script: + - go test -race -v -tags appengine ./... + - go: 1.12.x + env: GO111MODULE=off + install: + - go get github.com/stretchr/testify/assert + - go get golang.org/x/crypto/ssh/terminal + - go get golang.org/x/sys/unix + - go get golang.org/x/sys/windows + script: + - go test -race -v -tags appengine ./... From dc7874a90295908d4299e0c5d7aac52842b9615b Mon Sep 17 00:00:00 2001 From: Andrey Tcherepanov Date: Tue, 26 Mar 2019 10:19:59 -0600 Subject: [PATCH 456/547] Got rid of IsTerminal call to reduce external dependencies --- terminal_check_aix.go | 10 +++++----- terminal_check_bsd.go | 7 +++++++ terminal_check_linux.go | 7 +++++++ terminal_check_notappengine.go | 8 +++++--- 4 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 terminal_check_bsd.go create mode 100644 terminal_check_linux.go diff --git a/terminal_check_aix.go b/terminal_check_aix.go index 04fdb7ba3..948c38512 100644 --- a/terminal_check_aix.go +++ b/terminal_check_aix.go @@ -1,9 +1,9 @@ -// +build !appengine,!js,!windows,aix +// +build aix package logrus -import "io" +import ( + "golang.org/x/sys/unix" +) -func checkIfTerminal(w io.Writer) bool { - return false -} +const ioctlReadTermios = unix.TCGETS diff --git a/terminal_check_bsd.go b/terminal_check_bsd.go new file mode 100644 index 000000000..8f9c33094 --- /dev/null +++ b/terminal_check_bsd.go @@ -0,0 +1,7 @@ +// +build darwin dragonfly freebsd netbsd openbsd + +package logrus + +import "golang.org/x/sys/unix" + +const ioctlReadTermios = unix.TIOCGETA diff --git a/terminal_check_linux.go b/terminal_check_linux.go new file mode 100644 index 000000000..169019785 --- /dev/null +++ b/terminal_check_linux.go @@ -0,0 +1,7 @@ +// +build linux + +package logrus + +import "golang.org/x/sys/unix" + +const ioctlReadTermios = unix.TCGETS diff --git a/terminal_check_notappengine.go b/terminal_check_notappengine.go index d46556509..156a93b7d 100644 --- a/terminal_check_notappengine.go +++ b/terminal_check_notappengine.go @@ -1,4 +1,4 @@ -// +build !appengine,!js,!windows,!aix +// +build !appengine,!js,!windows package logrus @@ -6,13 +6,15 @@ import ( "io" "os" - "golang.org/x/crypto/ssh/terminal" + "golang.org/x/sys/unix" ) func checkIfTerminal(w io.Writer) bool { switch v := w.(type) { case *os.File: - return terminal.IsTerminal(int(v.Fd())) + _, err := unix.IoctlGetTermios(int(v.Fd()), ioctlReadTermios) + + return err == nil default: return false } From fa280de45868f1f9bc8def5137d8004d7a6fb4cc Mon Sep 17 00:00:00 2001 From: Andrey Tcherepanov Date: Tue, 26 Mar 2019 11:01:50 -0600 Subject: [PATCH 457/547] Removed golang.org/x/crypto refs --- .travis.yml | 4 ---- go.mod | 1 - go.sum | 2 -- 3 files changed, 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index ff85ac772..21c75b7e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,6 @@ matrix: - go: 1.10.x install: - go get github.com/stretchr/testify/assert - - go get golang.org/x/crypto/ssh/terminal - go get golang.org/x/sys/unix - go get golang.org/x/sys/windows script: @@ -22,7 +21,6 @@ matrix: env: GO111MODULE=off install: - go get github.com/stretchr/testify/assert - - go get golang.org/x/crypto/ssh/terminal - go get golang.org/x/sys/unix - go get golang.org/x/sys/windows script: @@ -37,7 +35,6 @@ matrix: env: GO111MODULE=off install: - go get github.com/stretchr/testify/assert - - go get golang.org/x/crypto/ssh/terminal - go get golang.org/x/sys/unix - go get golang.org/x/sys/windows script: @@ -45,7 +42,6 @@ matrix: - go: 1.10.x install: - go get github.com/stretchr/testify/assert - - go get golang.org/x/crypto/ssh/terminal - go get golang.org/x/sys/unix - go get golang.org/x/sys/windows script: diff --git a/go.mod b/go.mod index 94574cc63..8261a2b3a 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,5 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/objx v0.1.1 // indirect github.com/stretchr/testify v1.2.2 - golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 ) diff --git a/go.sum b/go.sum index 133d34ae1..2d787be60 100644 --- a/go.sum +++ b/go.sum @@ -9,7 +9,5 @@ github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= From 3bcdf8df8738c7bdcf7a28a0511425e16eb08a61 Mon Sep 17 00:00:00 2001 From: Andrey Tcherepanov Date: Tue, 26 Mar 2019 14:53:49 -0600 Subject: [PATCH 458/547] Moved moved unix-related parts into terminal --- internal/terminal/terminal_check_bsd.go | 13 +++++++++++++ internal/terminal/terminal_check_unix.go | 13 +++++++++++++ terminal_check_aix.go | 9 --------- terminal_check_bsd.go | 7 ------- terminal_check_linux.go | 7 ------- terminal_check_notappengine.go | 6 ++---- 6 files changed, 28 insertions(+), 27 deletions(-) create mode 100644 internal/terminal/terminal_check_bsd.go create mode 100644 internal/terminal/terminal_check_unix.go delete mode 100644 terminal_check_aix.go delete mode 100644 terminal_check_bsd.go delete mode 100644 terminal_check_linux.go diff --git a/internal/terminal/terminal_check_bsd.go b/internal/terminal/terminal_check_bsd.go new file mode 100644 index 000000000..6a47df6d4 --- /dev/null +++ b/internal/terminal/terminal_check_bsd.go @@ -0,0 +1,13 @@ +// +build darwin dragonfly freebsd netbsd openbsd + +package terminal + +import "golang.org/x/sys/unix" + +const ioctlReadTermios = unix.TIOCGETA + +func IsTerminal(fd int) bool { + _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) + return err == nil +} + diff --git a/internal/terminal/terminal_check_unix.go b/internal/terminal/terminal_check_unix.go new file mode 100644 index 000000000..f30ea8784 --- /dev/null +++ b/internal/terminal/terminal_check_unix.go @@ -0,0 +1,13 @@ +// +build linux aix + +package terminal + +import "golang.org/x/sys/unix" + +const ioctlReadTermios = unix.TCGETS + +func IsTerminal(fd int) bool { + _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) + return err == nil +} + diff --git a/terminal_check_aix.go b/terminal_check_aix.go deleted file mode 100644 index 948c38512..000000000 --- a/terminal_check_aix.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build aix - -package logrus - -import ( - "golang.org/x/sys/unix" -) - -const ioctlReadTermios = unix.TCGETS diff --git a/terminal_check_bsd.go b/terminal_check_bsd.go deleted file mode 100644 index 8f9c33094..000000000 --- a/terminal_check_bsd.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build darwin dragonfly freebsd netbsd openbsd - -package logrus - -import "golang.org/x/sys/unix" - -const ioctlReadTermios = unix.TIOCGETA diff --git a/terminal_check_linux.go b/terminal_check_linux.go deleted file mode 100644 index 169019785..000000000 --- a/terminal_check_linux.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build linux - -package logrus - -import "golang.org/x/sys/unix" - -const ioctlReadTermios = unix.TCGETS diff --git a/terminal_check_notappengine.go b/terminal_check_notappengine.go index 156a93b7d..6f28f89f5 100644 --- a/terminal_check_notappengine.go +++ b/terminal_check_notappengine.go @@ -6,15 +6,13 @@ import ( "io" "os" - "golang.org/x/sys/unix" + "github.com/sirupsen/logrus/internal/terminal" ) func checkIfTerminal(w io.Writer) bool { switch v := w.(type) { case *os.File: - _, err := unix.IoctlGetTermios(int(v.Fd()), ioctlReadTermios) - - return err == nil + return terminal.IsTerminal(int(v.Fd())) default: return false } From 3ffa802ad389d5accb74054f0578b130979fd8a2 Mon Sep 17 00:00:00 2001 From: Andrey Tcherepanov Date: Wed, 27 Mar 2019 10:59:38 -0600 Subject: [PATCH 459/547] Updated travis.yml --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 21c75b7e1..2442f0e9a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,7 +56,6 @@ matrix: env: GO111MODULE=off install: - go get github.com/stretchr/testify/assert - - go get golang.org/x/crypto/ssh/terminal - go get golang.org/x/sys/unix - go get golang.org/x/sys/windows script: @@ -71,7 +70,6 @@ matrix: env: GO111MODULE=off install: - go get github.com/stretchr/testify/assert - - go get golang.org/x/crypto/ssh/terminal - go get golang.org/x/sys/unix - go get golang.org/x/sys/windows script: From 1d44a57dfa8074c2b4af3e898e6ec20bb119927b Mon Sep 17 00:00:00 2001 From: Jessica Paczuski Date: Thu, 28 Mar 2019 11:32:49 +0100 Subject: [PATCH 460/547] Move terminal package fixes issue where terminal_check_notappengine.go can't access terminal package since terminal package is in an internal package --- {internal/terminal => terminal}/terminal_check_bsd.go | 0 {internal/terminal => terminal}/terminal_check_unix.go | 0 terminal_check_notappengine.go | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename {internal/terminal => terminal}/terminal_check_bsd.go (100%) rename {internal/terminal => terminal}/terminal_check_unix.go (100%) diff --git a/internal/terminal/terminal_check_bsd.go b/terminal/terminal_check_bsd.go similarity index 100% rename from internal/terminal/terminal_check_bsd.go rename to terminal/terminal_check_bsd.go diff --git a/internal/terminal/terminal_check_unix.go b/terminal/terminal_check_unix.go similarity index 100% rename from internal/terminal/terminal_check_unix.go rename to terminal/terminal_check_unix.go diff --git a/terminal_check_notappengine.go b/terminal_check_notappengine.go index 6f28f89f5..b61be8d55 100644 --- a/terminal_check_notappengine.go +++ b/terminal_check_notappengine.go @@ -6,7 +6,7 @@ import ( "io" "os" - "github.com/sirupsen/logrus/internal/terminal" + "github.com/sirupsen/logrus/terminal" ) func checkIfTerminal(w io.Writer) bool { From f1805d0a626bc31501b92dfe28784d2b60cff8f5 Mon Sep 17 00:00:00 2001 From: Andrey Tcherepanov Date: Thu, 28 Mar 2019 13:14:38 -0600 Subject: [PATCH 461/547] Test more platforms It would be 5 * 3 = 15 runs --- .travis.yml | 89 ++++++++++------------------------------------------- 1 file changed, 17 insertions(+), 72 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2442f0e9a..7e54dc6e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,76 +1,21 @@ language: go go_import_path: github.com/sirupsen/logrus +git: + depth: 1 env: - - GOMAXPROCS=4 GORACE=halt_on_error=1 + - GO111MODULE=on + - GO111MODULE=off +go: [ 1.10.x, 1.11.x, 1.12.x ] +os: [ linux, osx, windows ] matrix: - include: - - go: 1.10.x - install: - - go get github.com/stretchr/testify/assert - - go get golang.org/x/sys/unix - - go get golang.org/x/sys/windows - script: - - go test -race -v ./... - - go: 1.11.x - env: GO111MODULE=on - install: - - go mod download - script: - - go test -race -v ./... - - go: 1.11.x - env: GO111MODULE=off - install: - - go get github.com/stretchr/testify/assert - - go get golang.org/x/sys/unix - - go get golang.org/x/sys/windows - script: - - go test -race -v ./... - - go: 1.12.x - env: GO111MODULE=on - install: - - go mod download - script: - - go test -race -v ./... - - go: 1.12.x - env: GO111MODULE=off - install: - - go get github.com/stretchr/testify/assert - - go get golang.org/x/sys/unix - - go get golang.org/x/sys/windows - script: - - go test -race -v ./... - - go: 1.10.x - install: - - go get github.com/stretchr/testify/assert - - go get golang.org/x/sys/unix - - go get golang.org/x/sys/windows - script: - - go test -race -v -tags appengine ./... - - go: 1.11.x - env: GO111MODULE=on - install: - - go mod download - script: - - go test -race -v -tags appengine ./... - - go: 1.11.x - env: GO111MODULE=off - install: - - go get github.com/stretchr/testify/assert - - go get golang.org/x/sys/unix - - go get golang.org/x/sys/windows - script: - - go test -race -v -tags appengine ./... - - go: 1.12.x - env: GO111MODULE=on - install: - - go mod download - script: - - go test -race -v -tags appengine ./... - - go: 1.12.x - env: GO111MODULE=off - install: - - go get github.com/stretchr/testify/assert - - go get golang.org/x/sys/unix - - go get golang.org/x/sys/windows - script: - - go test -race -v -tags appengine ./... + exclude: + - env: GO111MODULE=on + go: 1.10.x +install: + - if [[ "$GO111MODULE" == "on" ]]; then go mod download; fi + - if [[ "$GO111MODULE" == "off" ]]; then go get github.com/stretchr/testify/assert golang.org/x/sys/unix github.com/konsorten/go-windows-terminal-sequences; fi +script: + - export GOMAXPROCS=4 + - export GORACE=halt_on_error=1 + - go test -race -v ./... + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then go test -race -v -tags appengine ./... ; fi From 224112b6f637f921794d48be6efe655862d2aa3d Mon Sep 17 00:00:00 2001 From: Haoran Xu Date: Fri, 29 Mar 2019 14:04:26 +0800 Subject: [PATCH 462/547] return new entry for Entry.WithContext --- entry.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/entry.go b/entry.go index 3d2c9e0fb..63e25583c 100644 --- a/entry.go +++ b/entry.go @@ -103,8 +103,7 @@ func (entry *Entry) WithError(err error) *Entry { // Add a context to the Entry. func (entry *Entry) WithContext(ctx context.Context) *Entry { - entry.Context = ctx - return entry + return &Entry{Logger: entry.Logger, Data: entry.Data, Time: entry.Time, err: entry.err, Context: ctx} } // Add a single field to the Entry. From c01b1aec51ea7f7a63d7f1a8ea7b153ff4b9c7e1 Mon Sep 17 00:00:00 2001 From: Andrey Tcherepanov Date: Mon, 1 Apr 2019 10:15:09 -0600 Subject: [PATCH 463/547] Move files to main directory --- terminal/terminal_check_bsd.go => terminal_check_bsd.go | 0 terminal/terminal_check_unix.go => terminal_check_unix.go | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename terminal/terminal_check_bsd.go => terminal_check_bsd.go (100%) rename terminal/terminal_check_unix.go => terminal_check_unix.go (100%) diff --git a/terminal/terminal_check_bsd.go b/terminal_check_bsd.go similarity index 100% rename from terminal/terminal_check_bsd.go rename to terminal_check_bsd.go diff --git a/terminal/terminal_check_unix.go b/terminal_check_unix.go similarity index 100% rename from terminal/terminal_check_unix.go rename to terminal_check_unix.go From 003af98bf096fc7575feabaff2878e4e913c1ec1 Mon Sep 17 00:00:00 2001 From: Andrey Tcherepanov Date: Mon, 1 Apr 2019 10:16:11 -0600 Subject: [PATCH 464/547] Make isTerminal un-exported --- terminal_check_bsd.go | 4 ++-- terminal_check_notappengine.go | 4 +--- terminal_check_unix.go | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/terminal_check_bsd.go b/terminal_check_bsd.go index 6a47df6d4..3c4f43f91 100644 --- a/terminal_check_bsd.go +++ b/terminal_check_bsd.go @@ -1,12 +1,12 @@ // +build darwin dragonfly freebsd netbsd openbsd -package terminal +package logrus import "golang.org/x/sys/unix" const ioctlReadTermios = unix.TIOCGETA -func IsTerminal(fd int) bool { +func isTerminal(fd int) bool { _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) return err == nil } diff --git a/terminal_check_notappengine.go b/terminal_check_notappengine.go index b61be8d55..7be2d87c5 100644 --- a/terminal_check_notappengine.go +++ b/terminal_check_notappengine.go @@ -5,14 +5,12 @@ package logrus import ( "io" "os" - - "github.com/sirupsen/logrus/terminal" ) func checkIfTerminal(w io.Writer) bool { switch v := w.(type) { case *os.File: - return terminal.IsTerminal(int(v.Fd())) + return isTerminal(int(v.Fd())) default: return false } diff --git a/terminal_check_unix.go b/terminal_check_unix.go index f30ea8784..355dc966f 100644 --- a/terminal_check_unix.go +++ b/terminal_check_unix.go @@ -1,12 +1,12 @@ // +build linux aix -package terminal +package logrus import "golang.org/x/sys/unix" const ioctlReadTermios = unix.TCGETS -func IsTerminal(fd int) bool { +func isTerminal(fd int) bool { _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) return err == nil } From d7e4ba4124379b9253f0b3fd43bf724c04f589bc Mon Sep 17 00:00:00 2001 From: Haoran Xu Date: Tue, 2 Apr 2019 11:43:56 +0800 Subject: [PATCH 465/547] remove field if val is empty string for func and file field in text formatter --- text_formatter.go | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/text_formatter.go b/text_formatter.go index b3b1a30ca..1569161eb 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -73,9 +73,9 @@ type TextFormatter struct { FieldMap FieldMap // CallerPrettyfier can be set by the user to modify the content - // of the function and file keys in the json data when ReportCaller is + // of the function and file keys in the data when ReportCaller is // activated. If any of the returned value is the empty string the - // corresponding key will be removed from json fields. + // corresponding key will be removed from fields. CallerPrettyfier func(*runtime.Frame) (function string, file string) terminalInitOnce sync.Once @@ -133,14 +133,19 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError)) } if entry.HasCaller() { - fixedKeys = append(fixedKeys, - f.FieldMap.resolve(FieldKeyFunc), f.FieldMap.resolve(FieldKeyFile)) if f.CallerPrettyfier != nil { funcVal, fileVal = f.CallerPrettyfier(entry.Caller) } else { funcVal = entry.Caller.Function fileVal = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) } + + if funcVal != "" { + fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFunc)) + } + if fileVal != "" { + fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFile)) + } } if !f.DisableSorting { @@ -225,7 +230,6 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin entry.Message = strings.TrimSuffix(entry.Message, "\n") caller := "" - if entry.HasCaller() { funcVal := fmt.Sprintf("%s()", entry.Caller.Function) fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) @@ -233,7 +237,14 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin if f.CallerPrettyfier != nil { funcVal, fileVal = f.CallerPrettyfier(entry.Caller) } - caller = fileVal + " " + funcVal + + if fileVal == "" { + caller = funcVal + } else if funcVal == "" { + caller = fileVal + } else { + caller = fileVal + " " + funcVal + } } if f.DisableTimestamp { From 6e3c4664e4be5d9ab712eaa36a3e33cb1234a9f5 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Tue, 2 Apr 2019 18:14:07 +0200 Subject: [PATCH 466/547] Release 1.4.1 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9978b415a..f62cbd24a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# 1.4.1 +This new release introduces: + * Enhance TextFormatter to not print caller information when they are empty (#944) + * Remove dependency on golang.org/x/crypto (#932, #943) + +Fixes: + * Fix Entry.WithContext method to return a copy of the initial entry (#941) + # 1.4.0 This new release introduces: * Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848). From 2f33de81f9fd7ce7ec67ccab61362d7e144367be Mon Sep 17 00:00:00 2001 From: David Bariod Date: Wed, 3 Apr 2019 10:46:44 +0200 Subject: [PATCH 467/547] Fix solaris build --- terminal_check_solaris.go | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 terminal_check_solaris.go diff --git a/terminal_check_solaris.go b/terminal_check_solaris.go new file mode 100644 index 000000000..f6710b3bd --- /dev/null +++ b/terminal_check_solaris.go @@ -0,0 +1,11 @@ +package logrus + +import ( + "golang.org/x/sys/unix" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +func isTerminal(fd int) bool { + _, err := unix.IoctlGetTermio(fd, unix.TCGETA) + return err == nil +} From 0388f917e4ae630c11bb0893ca6a3d19ada3f32c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chigot?= Date: Tue, 23 Apr 2019 11:34:05 +0200 Subject: [PATCH 468/547] Update x/sys/unix to fix AIX support --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 8261a2b3a..12fdf9898 100644 --- a/go.mod +++ b/go.mod @@ -6,5 +6,5 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/objx v0.1.1 // indirect github.com/stretchr/testify v1.2.2 - golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 + golang.org/x/sys v0.0.0-20190422165155-953cdadca894 ) diff --git a/go.sum b/go.sum index 2d787be60..7976428d0 100644 --- a/go.sum +++ b/go.sum @@ -11,3 +11,5 @@ github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= From 94e8b252be53cf631f3d931a3a0c7f2b38a69e51 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Fri, 10 May 2019 06:44:02 +0200 Subject: [PATCH 469/547] remove go 1.10 from ci build matrix --- .travis.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7e54dc6e3..4fac79ae1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,12 +5,8 @@ git: env: - GO111MODULE=on - GO111MODULE=off -go: [ 1.10.x, 1.11.x, 1.12.x ] +go: [ 1.11.x, 1.12.x ] os: [ linux, osx, windows ] -matrix: - exclude: - - env: GO111MODULE=on - go: 1.10.x install: - if [[ "$GO111MODULE" == "on" ]]; then go mod download; fi - if [[ "$GO111MODULE" == "off" ]]; then go get github.com/stretchr/testify/assert golang.org/x/sys/unix github.com/konsorten/go-windows-terminal-sequences; fi From 96ec5a8e5463a88fffbc130c7de1d563cd306b01 Mon Sep 17 00:00:00 2001 From: "A. F" Date: Sat, 11 May 2019 00:40:04 +0200 Subject: [PATCH 470/547] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a4796eb07..2040b42e0 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ func main() { 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) + // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) // if err == nil { // log.Out = file // } else { From 5da6a45c51894de565b3897f08108394a593fcac Mon Sep 17 00:00:00 2001 From: Nicolas Lepage <19571875+nlepage@users.noreply.github.com> Date: Tue, 14 May 2019 09:13:07 +0200 Subject: [PATCH 471/547] Add a checkTerminal for nacl to support running on play.golang.org --- terminal_check_nacl.go | 11 +++++++++++ terminal_check_notappengine.go | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 terminal_check_nacl.go diff --git a/terminal_check_nacl.go b/terminal_check_nacl.go new file mode 100644 index 000000000..59d1e2c4e --- /dev/null +++ b/terminal_check_nacl.go @@ -0,0 +1,11 @@ +// +build nacl + +package logrus + +import ( + "io" +) + +func checkIfTerminal(w io.Writer) bool { + return false +} diff --git a/terminal_check_notappengine.go b/terminal_check_notappengine.go index 7be2d87c5..a0b571ad8 100644 --- a/terminal_check_notappengine.go +++ b/terminal_check_notappengine.go @@ -1,4 +1,4 @@ -// +build !appengine,!js,!windows +// +build !appengine,!js,!windows,!nacl package logrus From 7767e3dd260db597d17f52c652f5622a2ed8a1f2 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sat, 18 May 2019 10:27:12 +0200 Subject: [PATCH 472/547] add full cross compilation in travis (#963) * add full cross compilation in travis * reduce the travis build matrix * disable cross build for plan9 and nacl --- .travis.yml | 10 +++++++++- travis/cross_build.sh | 5 +++++ travis/install.sh | 11 +++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100755 travis/cross_build.sh create mode 100755 travis/install.sh diff --git a/.travis.yml b/.travis.yml index 4fac79ae1..848938a6d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,11 +6,19 @@ env: - GO111MODULE=on - GO111MODULE=off go: [ 1.11.x, 1.12.x ] -os: [ linux, osx, windows ] +os: [ linux, osx ] +matrix: + exclude: + - go: 1.12.x + env: GO111MODULE=off + - go: 1.11.x + os: osx install: + - ./travis/install.sh - if [[ "$GO111MODULE" == "on" ]]; then go mod download; fi - if [[ "$GO111MODULE" == "off" ]]; then go get github.com/stretchr/testify/assert golang.org/x/sys/unix github.com/konsorten/go-windows-terminal-sequences; fi script: + - ./travis/cross_build.sh - export GOMAXPROCS=4 - export GORACE=halt_on_error=1 - go test -race -v ./... diff --git a/travis/cross_build.sh b/travis/cross_build.sh new file mode 100755 index 000000000..7bb944987 --- /dev/null +++ b/travis/cross_build.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +if [[ "$TRAVIS_GO_VERSION" =~ ^1.\12\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + /tmp/gox/gox -build-lib -all -os '!plan9 !nacl' +fi diff --git a/travis/install.sh b/travis/install.sh new file mode 100755 index 000000000..07f453278 --- /dev/null +++ b/travis/install.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +set -e + +if [[ "$TRAVIS_GO_VERSION" =~ ^1.\12\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + git clone https://github.com/dgsb/gox.git /tmp/gox + pushd /tmp/gox + git checkout new_master + go build ./ + popd +fi From 0a688f9e17953fc965dc17895ffc007766351f8f Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sat, 18 May 2019 11:44:18 +0200 Subject: [PATCH 473/547] fix build break for plan9 --- go.sum | 1 + terminal_check_nacl.go | 11 ----------- ...heck_js.go => terminal_check_no_terminal.go | 2 +- terminal_check_notappengine.go | 2 +- terminal_check_windows.go | 18 ++++++++++++++++-- terminal_notwindows.go | 8 -------- terminal_windows.go | 18 ------------------ text_formatter.go | 4 ---- travis/cross_build.sh | 2 +- 9 files changed, 20 insertions(+), 46 deletions(-) delete mode 100644 terminal_check_nacl.go rename terminal_check_js.go => terminal_check_no_terminal.go (79%) delete mode 100644 terminal_notwindows.go delete mode 100644 terminal_windows.go diff --git a/go.sum b/go.sum index 7976428d0..596c318b9 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe h1:CHRGQ8V7OlCYtwaKPJi3iA7J+YdNKdo8j7nG5IgDhjs= github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/terminal_check_nacl.go b/terminal_check_nacl.go deleted file mode 100644 index 59d1e2c4e..000000000 --- a/terminal_check_nacl.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build nacl - -package logrus - -import ( - "io" -) - -func checkIfTerminal(w io.Writer) bool { - return false -} diff --git a/terminal_check_js.go b/terminal_check_no_terminal.go similarity index 79% rename from terminal_check_js.go rename to terminal_check_no_terminal.go index 0c209750a..97af92c68 100644 --- a/terminal_check_js.go +++ b/terminal_check_no_terminal.go @@ -1,4 +1,4 @@ -// +build js +// +build js nacl plan9 package logrus diff --git a/terminal_check_notappengine.go b/terminal_check_notappengine.go index a0b571ad8..3293fb3ca 100644 --- a/terminal_check_notappengine.go +++ b/terminal_check_notappengine.go @@ -1,4 +1,4 @@ -// +build !appengine,!js,!windows,!nacl +// +build !appengine,!js,!windows,!nacl,!plan9 package logrus diff --git a/terminal_check_windows.go b/terminal_check_windows.go index 3b9d2864c..572889db2 100644 --- a/terminal_check_windows.go +++ b/terminal_check_windows.go @@ -6,15 +6,29 @@ import ( "io" "os" "syscall" + + sequences "github.com/konsorten/go-windows-terminal-sequences" ) +func initTerminal(w io.Writer) { + switch v := w.(type) { + case *os.File: + sequences.EnableVirtualTerminalProcessing(syscall.Handle(v.Fd()), true) + } +} + func checkIfTerminal(w io.Writer) bool { + var ret bool switch v := w.(type) { case *os.File: var mode uint32 err := syscall.GetConsoleMode(syscall.Handle(v.Fd()), &mode) - return err == nil + ret = (err == nil) default: - return false + ret = false + } + if ret { + initTerminal(w) } + return ret } diff --git a/terminal_notwindows.go b/terminal_notwindows.go deleted file mode 100644 index 3dbd23720..000000000 --- a/terminal_notwindows.go +++ /dev/null @@ -1,8 +0,0 @@ -// +build !windows - -package logrus - -import "io" - -func initTerminal(w io.Writer) { -} diff --git a/terminal_windows.go b/terminal_windows.go deleted file mode 100644 index b4ef5286c..000000000 --- a/terminal_windows.go +++ /dev/null @@ -1,18 +0,0 @@ -// +build !appengine,!js,windows - -package logrus - -import ( - "io" - "os" - "syscall" - - sequences "github.com/konsorten/go-windows-terminal-sequences" -) - -func initTerminal(w io.Writer) { - switch v := w.(type) { - case *os.File: - sequences.EnableVirtualTerminalProcessing(syscall.Handle(v.Fd()), true) - } -} diff --git a/text_formatter.go b/text_formatter.go index 1569161eb..e01587c43 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -84,10 +84,6 @@ type TextFormatter struct { func (f *TextFormatter) init(entry *Entry) { if entry.Logger != nil { f.isTerminal = checkIfTerminal(entry.Logger.Out) - - if f.isTerminal { - initTerminal(entry.Logger.Out) - } } } diff --git a/travis/cross_build.sh b/travis/cross_build.sh index 7bb944987..545d8c329 100755 --- a/travis/cross_build.sh +++ b/travis/cross_build.sh @@ -1,5 +1,5 @@ #!/bin/bash if [[ "$TRAVIS_GO_VERSION" =~ ^1.\12\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - /tmp/gox/gox -build-lib -all -os '!plan9 !nacl' + /tmp/gox/gox -build-lib -all fi From afcf02ac607cb26ed37c96388056ddca512e7f80 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sat, 18 May 2019 12:38:48 +0200 Subject: [PATCH 474/547] Release 1.4.2 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f62cbd24a..51a7ab0ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +# 1.4.2 + * Fixes build break for plan9, nacl, solaris # 1.4.1 This new release introduces: * Enhance TextFormatter to not print caller information when they are empty (#944) From e11b4bc1e9055124f04ba7dfe5f459dc69d55c6d Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Tue, 4 Jun 2019 20:08:45 -0700 Subject: [PATCH 475/547] tracking commit From dfe5f93929fb690e8ef944f76258ef842af4e467 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Tue, 4 Jun 2019 20:09:59 -0700 Subject: [PATCH 476/547] tracking commit From c7c0b0ef4446b4d0cb383688b34fd4c07de1eb5f Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Wed, 5 Jun 2019 00:10:46 -0700 Subject: [PATCH 477/547] add implementation and tests --- text_formatter.go | 9 ++++- text_formatter_test.go | 88 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/text_formatter.go b/text_formatter.go index e01587c43..fb1349907 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -57,6 +57,10 @@ type TextFormatter struct { // Disables the truncation of the level text to 4 characters. DisableLevelTruncation bool + // PadLevelText Adds padding the level text so that all the levels output at the same length + // PadLevelText is a superset of the DisableLevelTruncation option + PadLevelText bool + // QuoteEmptyFields will wrap empty fields in quotes if true QuoteEmptyFields bool @@ -217,9 +221,12 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin } levelText := strings.ToUpper(entry.Level.String()) - if !f.DisableLevelTruncation { + if !f.DisableLevelTruncation && !f.PadLevelText { levelText = levelText[0:4] } + if f.PadLevelText { + levelText = fmt.Sprintf("%-7s", levelText) + } // Remove a single newline if it already exists in the message to keep // the behavior of logrus text_formatter the same as the stdlib log package diff --git a/text_formatter_test.go b/text_formatter_test.go index 9c5e6f0a1..e48e43097 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -172,6 +172,94 @@ func TestDisableLevelTruncation(t *testing.T) { checkDisableTruncation(false, InfoLevel) } +func TestPadLevelText(t *testing.T) { + // A note for future maintainers / committers: + // + // This test denormalizes the level text as a part of its assertions. + // Because of that, its not really a "unit test" of the PadLevelText functionality. + // So! Many apologies to the potential future person who has to rewrite this test + // when they are changing some completely unrelated functionality. + params := []struct { + name string + level Level + paddedLevelText string + }{ + { + name: "PanicLevel", + level: PanicLevel, + paddedLevelText: "PANIC ", // 2 extra spaces + }, + { + name: "FatalLevel", + level: FatalLevel, + paddedLevelText: "FATAL ", // 2 extra spaces + }, + { + name: "ErrorLevel", + level: ErrorLevel, + paddedLevelText: "ERROR ", // 2 extra spaces + }, + { + name: "WarnLevel", + level: WarnLevel, + // WARNING is already the max length, so we don't need to assert a paddedLevelText + }, + { + name: "DebugLevel", + level: DebugLevel, + paddedLevelText: "DEBUG ", // 2 extra spaces + }, + { + name: "TraceLevel", + level: TraceLevel, + paddedLevelText: "TRACE ", // 2 extra spaces + }, + { + name: "InfoLevel", + level: InfoLevel, + paddedLevelText: "INFO ", // 3 extra spaces + }, + } + + // We create a "default" TextFormatter to do a control case test + // and a TextFormatter with PadLevelText, which is the flag we are testing here + tfDefault := TextFormatter{} + tfWithPadding := TextFormatter{PadLevelText: true} + + for _, val := range params { + t.Run(val.name, func(t *testing.T) { + // TextFormatter writes into these bytes.Buffers, and we make assertions about their contents later + var bytesDefault bytes.Buffer + var bytesWithPadding bytes.Buffer + + // The TextFormatter instance and the bytes.Buffer instance are different here + // all the other arguments are the same + tfDefault.printColored(&bytesDefault, &Entry{Level: val.level}, []string{}, nil, "") + tfWithPadding.printColored(&bytesWithPadding, &Entry{Level: val.level}, []string{}, nil, "") + + // turn the bytes back into a string so that we can actually work with the data + logLineDefault := (&bytesDefault).String() + logLineWithPadding := (&bytesWithPadding).String() + + // Control: the level text should not be padded by default + if val.paddedLevelText != "" && strings.Contains(logLineDefault, val.paddedLevelText) { + t.Errorf("log line \"%s\" should not contain the padded level text \"%s\" by default", logLineDefault, val.paddedLevelText) + } + + // Assertion: the level text should still contain the string representation of the level + if !strings.Contains(strings.ToLower(logLineWithPadding), val.level.String()) { + t.Errorf("log line \"%s\" should contain the level text \"%s\" when padding is enabled", logLineWithPadding, val.level.String()) + } + + // Assertion: the level text should be in its padded form now + if val.paddedLevelText != "" && !strings.Contains(logLineWithPadding, val.paddedLevelText) { + t.Errorf("log line \"%s\" should contain the padded level text \"%s\" when padding is enabled", logLineWithPadding, val.paddedLevelText) + } + + }) + } +} + func TestDisableTimestampWithColoredOutput(t *testing.T) { tf := &TextFormatter{DisableTimestamp: true, ForceColors: true} From 3f8c3576f3a85073d70b7c10387338f5a71cfaa0 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Wed, 5 Jun 2019 00:27:10 -0700 Subject: [PATCH 478/547] update comments --- text_formatter_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text_formatter_test.go b/text_formatter_test.go index e48e43097..775f4aad6 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -221,8 +221,8 @@ func TestPadLevelText(t *testing.T) { }, } - // We create a "default" TextFormatter to do a control case test - // and a TextFormatter with PadLevelText, which is the flag we are testing here + // We create a "default" TextFormatter to do a control test. + // We also create a TextFormatter with PadLevelText, which is the parameter we want to do our most relevant assertions against. tfDefault := TextFormatter{} tfWithPadding := TextFormatter{PadLevelText: true} From 1f57e96c950817c16a4938250c3968cd65ab3fd5 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Wed, 5 Jun 2019 00:33:48 -0700 Subject: [PATCH 479/547] update the readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2040b42e0..48d64a004 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcr ``` Note that this does add measurable overhead - the cost will depend on the version of Go, but is between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your -environment via benchmarks: +environment via benchmarks: ``` go test -bench=.*CallerTracing ``` @@ -354,6 +354,7 @@ The built-in logging formatters are: [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable). * When colors are enabled, levels are truncated to 4 characters by default. To disable truncation set the `DisableLevelTruncation` field to `true`. + * When outputting to a TTY, its often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this functionality, by adding padding to the level text. * 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). From 837edcd070434b5011f1d666bee57b6ff9e2e53f Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Wed, 5 Jun 2019 00:36:14 -0700 Subject: [PATCH 480/547] add a space back --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 48d64a004..c5cd0e4d0 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcr ``` Note that this does add measurable overhead - the cost will depend on the version of Go, but is between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your -environment via benchmarks: +environment via benchmarks: ``` go test -bench=.*CallerTracing ``` From c0f949bcf2a7d57dd06fc036dd9cee329dcf5ed9 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Wed, 5 Jun 2019 00:41:05 -0700 Subject: [PATCH 481/547] wording shift --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c5cd0e4d0..08c3269b0 100644 --- a/README.md +++ b/README.md @@ -354,7 +354,7 @@ The built-in logging formatters are: [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable). * When colors are enabled, levels are truncated to 4 characters by default. To disable truncation set the `DisableLevelTruncation` field to `true`. - * When outputting to a TTY, its often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this functionality, by adding padding to the level text. + * When outputting to a TTY, its often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this behavior, by adding padding to the level text. * 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). From 6150605a676b1640e8aac1eb639ff520023686b8 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Wed, 5 Jun 2019 00:49:35 -0700 Subject: [PATCH 482/547] bump ci From 7dfe1955c6c0a3e57570f9ab915373d03f9bbaff Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Mon, 24 Jun 2019 20:42:20 -0700 Subject: [PATCH 483/547] dynamically space the level text --- text_formatter.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/text_formatter.go b/text_formatter.go index fb1349907..a4b65e8db 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -6,6 +6,7 @@ import ( "os" "runtime" "sort" + "strconv" "strings" "sync" "time" @@ -83,12 +84,22 @@ type TextFormatter struct { CallerPrettyfier func(*runtime.Frame) (function string, file string) terminalInitOnce sync.Once + + // The max length of the level text, generated dynamically on init + levelTextMaxLength int } func (f *TextFormatter) init(entry *Entry) { if entry.Logger != nil { f.isTerminal = checkIfTerminal(entry.Logger.Out) } + // Get the max length of the level text + for _, level := range AllLevels { + levelTextLength := len(level.String()) + if levelTextLength > f.levelTextMaxLength { + f.levelTextMaxLength = levelTextLength + } + } } func (f *TextFormatter) isColored() bool { @@ -225,7 +236,13 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin levelText = levelText[0:4] } if f.PadLevelText { - levelText = fmt.Sprintf("%-7s", levelText) + // Generates the format string used in the next line, for example "%-6s" or "%-7s". + // Based on the max level text length. + formatString := "%-" + strconv.Itoa(f.levelTextMaxLength) + "s" + // Formats the level text by appending spaces up to the max length, for example: + // - "INFO " + // - "WARNING" + levelText = fmt.Sprintf(formatString, levelText) } // Remove a single newline if it already exists in the message to keep From 48c7b5ee60e587b1ae675a1fa185ea60fd228f41 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Mon, 24 Jun 2019 20:50:37 -0700 Subject: [PATCH 484/547] init the loggers in tests --- text_formatter_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/text_formatter_test.go b/text_formatter_test.go index 775f4aad6..d04fbcd44 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -233,8 +233,11 @@ func TestPadLevelText(t *testing.T) { var bytesWithPadding bytes.Buffer // The TextFormatter instance and the bytes.Buffer instance are different here - // all the other arguments are the same + // all the other arguments are the same. We also initialize them so that they + // fill in the value of levelTextMaxLength. + tfDefault.init(&Entry{}) tfDefault.printColored(&bytesDefault, &Entry{Level: val.level}, []string{}, nil, "") + tfWithPadding.init(&Entry{}) tfWithPadding.printColored(&bytesWithPadding, &Entry{Level: val.level}, []string{}, nil, "") // turn the bytes back into a string so that we can actually work with the data From b0c06e2d8c96f5356e4a6b91066024385cecf0d3 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Wed, 26 Jun 2019 20:37:17 -0700 Subject: [PATCH 485/547] avoid escapes! h/t @therealplato --- text_formatter_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text_formatter_test.go b/text_formatter_test.go index d04fbcd44..99b5d8cde 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -246,17 +246,17 @@ func TestPadLevelText(t *testing.T) { // Control: the level text should not be padded by default if val.paddedLevelText != "" && strings.Contains(logLineDefault, val.paddedLevelText) { - t.Errorf("log line \"%s\" should not contain the padded level text \"%s\" by default", logLineDefault, val.paddedLevelText) + t.Errorf("log line %q should not contain the padded level text %q by default", logLineDefault, val.paddedLevelText) } // Assertion: the level text should still contain the string representation of the level if !strings.Contains(strings.ToLower(logLineWithPadding), val.level.String()) { - t.Errorf("log line \"%s\" should contain the level text \"%s\" when padding is enabled", logLineWithPadding, val.level.String()) + t.Errorf("log line %q should contain the level text %q when padding is enabled", logLineWithPadding, val.level.String()) } // Assertion: the level text should be in its padded form now if val.paddedLevelText != "" && !strings.Contains(logLineWithPadding, val.paddedLevelText) { - t.Errorf("log line \"%s\" should contain the padded level text \"%s\" when padding is enabled", logLineWithPadding, val.paddedLevelText) + t.Errorf("log line %q should contain the padded level text %q when padding is enabled", logLineWithPadding, val.paddedLevelText) } }) From 7a2b4698eb011d3c22b6389a79278f022e2fcd80 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Wed, 26 Jun 2019 20:48:04 -0700 Subject: [PATCH 486/547] len => RuneCount note: this is not currently easily testable without a larger diff that refactors the levels --- text_formatter.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text_formatter.go b/text_formatter.go index a4b65e8db..f08563e45 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -10,6 +10,7 @@ import ( "strings" "sync" "time" + "unicode/utf8" ) const ( @@ -95,7 +96,7 @@ func (f *TextFormatter) init(entry *Entry) { } // Get the max length of the level text for _, level := range AllLevels { - levelTextLength := len(level.String()) + levelTextLength := utf8.RuneCount([]byte(level.String())) if levelTextLength > f.levelTextMaxLength { f.levelTextMaxLength = levelTextLength } From b82478b3c24c51224b23106bdad02292ea2fad4e Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 27 Jun 2019 18:35:00 -0700 Subject: [PATCH 487/547] its => it's --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 08c3269b0..e7450a88c 100644 --- a/README.md +++ b/README.md @@ -354,7 +354,7 @@ The built-in logging formatters are: [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable). * When colors are enabled, levels are truncated to 4 characters by default. To disable truncation set the `DisableLevelTruncation` field to `true`. - * When outputting to a TTY, its often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this behavior, by adding padding to the level text. + * When outputting to a TTY, it's often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this behavior, by adding padding to the level text. * 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). From a5f81be99b7460e1282ad6463717df5aa7cb1613 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 20 Jul 2019 03:27:21 +0200 Subject: [PATCH 488/547] Fixed ineffectual assignment in test Don't assign l if it's not being checked/used afterwards. --- logrus_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logrus_test.go b/logrus_test.go index 72b6ea254..51f09a221 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -539,7 +539,7 @@ func TestParseLevel(t *testing.T) { assert.Nil(t, err) assert.Equal(t, TraceLevel, l) - l, err = ParseLevel("invalid") + _, err = ParseLevel("invalid") assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error()) } From de4fc03da43dc4541d48512d15718e0d92a4b001 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 20 Jul 2019 03:29:10 +0200 Subject: [PATCH 489/547] Avoid unnecessary conversion No need to convert here. --- logrus.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logrus.go b/logrus.go index 8644761f7..2f16224cb 100644 --- a/logrus.go +++ b/logrus.go @@ -51,7 +51,7 @@ func (level *Level) UnmarshalText(text []byte) error { return err } - *level = Level(l) + *level = l return nil } From eee113859378cbf1cc98d11474415b6e75682d3d Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Wed, 7 Aug 2019 06:34:36 -0400 Subject: [PATCH 490/547] readme: we have great maintainers now --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index e7450a88c..7aa7bab31 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,6 @@ 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 -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 5a95a722196746bc57b65ca5cef1d0bfc20cd54f Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Thu, 5 Sep 2019 16:09:16 -0700 Subject: [PATCH 491/547] return early This makes it easier to read / understand and is more idiomatic. --- entry.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/entry.go b/entry.go index 63e25583c..7e2c8993a 100644 --- a/entry.go +++ b/entry.go @@ -255,11 +255,10 @@ func (entry *Entry) write() { serialized, err := entry.Logger.Formatter.Format(entry) if err != nil { fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) - } else { - _, err = entry.Logger.Out.Write(serialized) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) - } + return + } + if _, err = entry.Logger.Out.Write(serialized); err != nil { + fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) } } From be5deb771eaf91e04ea0b8eb65ee273d090e9a9f Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Thu, 5 Sep 2019 19:34:46 -0700 Subject: [PATCH 492/547] some minimal documentation for Logger.Writer{,Level} This also includes two examples extracted from the readme. --- writer.go | 6 ++++++ writer_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 writer_test.go diff --git a/writer.go b/writer.go index 9e1f75135..72e8e3a1b 100644 --- a/writer.go +++ b/writer.go @@ -6,10 +6,16 @@ import ( "runtime" ) +// Writer at INFO level. See WriterLevel for details. func (logger *Logger) Writer() *io.PipeWriter { return logger.WriterLevel(InfoLevel) } +// WriterLevel returns an io.Writer that can be used to write arbitrary text to +// the logger at the given log level. Each line written to the writer will be +// printed in the usual way using formatters and hooks. The writer is part of an +// io.Pipe and it is the callers responsibility to close the writer when done. +// This can be used to override the standard library logger easily. func (logger *Logger) WriterLevel(level Level) *io.PipeWriter { return NewEntry(logger).WriterLevel(level) } diff --git a/writer_test.go b/writer_test.go new file mode 100644 index 000000000..5c34927da --- /dev/null +++ b/writer_test.go @@ -0,0 +1,34 @@ +package logrus_test + +import ( + "log" + "net/http" + + "github.com/sirupsen/logrus" +) + +func ExampleLogger_Writer_httpServer() { + logger := logrus.New() + w := logger.Writer() + defer w.Close() + + srv := http.Server{ + // create a stdlib log.Logger that writes to + // logrus.Logger. + ErrorLog: log.New(w, "", 0), + } + + if err := srv.ListenAndServe(); err != nil { + logger.Fatal(err) + } +} + +func ExampleLogger_Writer_stdlib() { + 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()) +} From 552178ee7c23596ee2863e0289bc184311d0dbcc Mon Sep 17 00:00:00 2001 From: Jonathan Hall Date: Thu, 19 Sep 2019 11:06:10 +0200 Subject: [PATCH 493/547] Add terminal_check_js.go file, for compatibility with GopherJS --- terminal_check_bsd.go | 2 +- terminal_check_js.go | 7 +++++++ terminal_check_unix.go | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 terminal_check_js.go diff --git a/terminal_check_bsd.go b/terminal_check_bsd.go index 3c4f43f91..499789984 100644 --- a/terminal_check_bsd.go +++ b/terminal_check_bsd.go @@ -1,4 +1,5 @@ // +build darwin dragonfly freebsd netbsd openbsd +// +build !js package logrus @@ -10,4 +11,3 @@ func isTerminal(fd int) bool { _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) return err == nil } - diff --git a/terminal_check_js.go b/terminal_check_js.go new file mode 100644 index 000000000..ebdae3ec6 --- /dev/null +++ b/terminal_check_js.go @@ -0,0 +1,7 @@ +// +build js + +package logrus + +func isTerminal(fd int) bool { + return false +} diff --git a/terminal_check_unix.go b/terminal_check_unix.go index 355dc966f..cc4fe6e31 100644 --- a/terminal_check_unix.go +++ b/terminal_check_unix.go @@ -1,4 +1,5 @@ // +build linux aix +// +build !js package logrus @@ -10,4 +11,3 @@ func isTerminal(fd int) bool { _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) return err == nil } - From 12b5167e4e97d5fc3accf681282dd0c6ed1c852e Mon Sep 17 00:00:00 2001 From: zxc Date: Sun, 29 Sep 2019 11:27:08 +0800 Subject: [PATCH 494/547] ReadMe.md file adds The Third Formatter link --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7aa7bab31..cec59a7da 100644 --- a/README.md +++ b/README.md @@ -362,6 +362,7 @@ Third party logging formatters: * [`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̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. * [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure. +* [`powerful-logrus-formatter`](https://github.com/zput/zxcTool). get fileName, log's line number and the latest function's name when print log; Sava log to files. You can define your formatter by implementing the `Formatter` interface, requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a From e6a32cf58e3674eb9f0b8292294a70e3d4c5682d Mon Sep 17 00:00:00 2001 From: Joel Williams Date: Fri, 4 Oct 2019 02:25:08 +0200 Subject: [PATCH 495/547] Fixed some typos in README.md Fixed a few typos and grammatical issues in the README.md. I hope this is helpful just to make a small improvement. --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7aa7bab31..56524bf03 100644 --- a/README.md +++ b/README.md @@ -267,7 +267,7 @@ func init() { ``` Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md). -A list of currently known of service hook can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks) +A list of currently known service hooks can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks) #### Level logging @@ -426,14 +426,14 @@ 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 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 be generated with different configs in different environments.| |[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 Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides: -* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook +* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just adds the `test` hook * a test logger (`test.NewNullLogger`) that just records log messages (and does not output any): ```go @@ -461,7 +461,7 @@ func TestSomething(t*testing.T){ Logrus can register one or more functions that will be called when any `fatal` level message is logged. The registered handlers will be executed before -logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need +logrus performs an `os.Exit(1)`. This behavior may be helpful if callers need to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted. ``` @@ -486,6 +486,6 @@ Situation when locking is not needed includes: 1) logger.Out is protected by locks. - 2) logger.Out is a os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allow multi-thread/multi-process writing) + 2) logger.Out is an os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allows multi-thread/multi-process writing) (Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/) From 855d0ce7b07e14e2bc03c90d9301bfd4c548986c Mon Sep 17 00:00:00 2001 From: Pantelis Sampaziotis Date: Fri, 11 Oct 2019 16:42:11 +0300 Subject: [PATCH 496/547] add Go 1.13 in travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 848938a6d..1d46e9a08 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ git: env: - GO111MODULE=on - GO111MODULE=off -go: [ 1.11.x, 1.12.x ] +go: [ 1.11.x, 1.12.x, 1.13.x ] os: [ linux, osx ] matrix: exclude: From 8e5347380cc24da0372ba01b75fe7747c41499c1 Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Sun, 13 Oct 2019 16:39:38 -0700 Subject: [PATCH 497/547] Disable modules, run on osx --- .travis.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1d46e9a08..b28e1e219 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,14 +5,16 @@ git: env: - GO111MODULE=on - GO111MODULE=off -go: [ 1.11.x, 1.12.x, 1.13.x ] -os: [ linux, osx ] +go: [1.11.x, 1.12.x, 1.13.x] +os: [linux, osx] matrix: exclude: + - go: 1.11.x + env: GO111MODULE=off - go: 1.12.x env: GO111MODULE=off - - go: 1.11.x - os: osx + - go: 1.13.x + env: GO111MODULE=off install: - ./travis/install.sh - if [[ "$GO111MODULE" == "on" ]]; then go mod download; fi From 243a30ebdbc13e5f99870306618723d4f6c75cad Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Sun, 13 Oct 2019 16:39:54 -0700 Subject: [PATCH 498/547] go mod verify; go mod tidy --- go.mod | 3 ++- go.sum | 6 ------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 12fdf9898..9ea6e841b 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,8 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.1 github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/objx v0.1.1 // indirect github.com/stretchr/testify v1.2.2 golang.org/x/sys v0.0.0-20190422165155-953cdadca894 ) + +go 1.13 diff --git a/go.sum b/go.sum index 596c318b9..95a3f07de 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe h1:CHRGQ8V7OlCYtwaKPJi3iA7J+YdNKdo8j7nG5IgDhjs= -github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= From 8450e418692b013a5b3557febe23e09f616032cb Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Sun, 13 Oct 2019 16:47:33 -0700 Subject: [PATCH 499/547] Enable all of these to see what fails --- .travis.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index b28e1e219..7fb79ffae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,14 +7,6 @@ env: - GO111MODULE=off go: [1.11.x, 1.12.x, 1.13.x] os: [linux, osx] -matrix: - exclude: - - go: 1.11.x - env: GO111MODULE=off - - go: 1.12.x - env: GO111MODULE=off - - go: 1.13.x - env: GO111MODULE=off install: - ./travis/install.sh - if [[ "$GO111MODULE" == "on" ]]; then go mod download; fi From 230ea16b6055c5a19e9bcd8eed7c35f7e2e9a421 Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Sun, 13 Oct 2019 16:51:57 -0700 Subject: [PATCH 500/547] get some other deps --- .travis.yml | 7 ++++++- travis/cross_build.sh | 2 +- travis/install.sh | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7fb79ffae..02f520b89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,12 @@ os: [linux, osx] install: - ./travis/install.sh - if [[ "$GO111MODULE" == "on" ]]; then go mod download; fi - - if [[ "$GO111MODULE" == "off" ]]; then go get github.com/stretchr/testify/assert golang.org/x/sys/unix github.com/konsorten/go-windows-terminal-sequences; fi + - if [[ "$GO111MODULE" == "off" ]]; then go get \ + github.com/stretchr/testify/assert \ + golang.org/x/sys/unix \ + github.com/konsorten/go-windows-terminal-sequences \ + github.com/hashicorp/go-version \ + github.com/hashicorp/go-version; fi script: - ./travis/cross_build.sh - export GOMAXPROCS=4 diff --git a/travis/cross_build.sh b/travis/cross_build.sh index 545d8c329..1ba63ab38 100755 --- a/travis/cross_build.sh +++ b/travis/cross_build.sh @@ -1,5 +1,5 @@ #!/bin/bash -if [[ "$TRAVIS_GO_VERSION" =~ ^1.\12\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then +if [[ "$TRAVIS_GO_VERSION" =~ ^1\.12\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then /tmp/gox/gox -build-lib -all fi diff --git a/travis/install.sh b/travis/install.sh index 07f453278..51b62a995 100755 --- a/travis/install.sh +++ b/travis/install.sh @@ -2,7 +2,7 @@ set -e -if [[ "$TRAVIS_GO_VERSION" =~ ^1.\12\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then +if [[ "$TRAVIS_GO_VERSION" =~ ^1\.12\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then git clone https://github.com/dgsb/gox.git /tmp/gox pushd /tmp/gox git checkout new_master From b5513696d662c09e138c38b918a9f865d038b782 Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Sun, 13 Oct 2019 16:56:28 -0700 Subject: [PATCH 501/547] pull all the install into a single location --- .travis.yml | 7 ------- travis/install.sh | 9 +++++++++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 02f520b89..8e2407065 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,13 +9,6 @@ go: [1.11.x, 1.12.x, 1.13.x] os: [linux, osx] install: - ./travis/install.sh - - if [[ "$GO111MODULE" == "on" ]]; then go mod download; fi - - if [[ "$GO111MODULE" == "off" ]]; then go get \ - github.com/stretchr/testify/assert \ - golang.org/x/sys/unix \ - github.com/konsorten/go-windows-terminal-sequences \ - github.com/hashicorp/go-version \ - github.com/hashicorp/go-version; fi script: - ./travis/cross_build.sh - export GOMAXPROCS=4 diff --git a/travis/install.sh b/travis/install.sh index 51b62a995..eaa511a06 100755 --- a/travis/install.sh +++ b/travis/install.sh @@ -9,3 +9,12 @@ if [[ "$TRAVIS_GO_VERSION" =~ ^1\.12\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; go build ./ popd fi + +if [[ "$GO111MODULE" == "on" ]]; then + go mod download +fi + +if [[ "$GO111MODULE" == "off" ]]; then + go get github.com/stretchr/testify/assert golang.org/x/sys/unix github.com/konsorten/go-windows-terminal-sequences + go get github.com/hashicorp/go-version github.com/hashicorp/go-version +fi From f2809110b17e2bf8aed479f7cc4eb3bf7f1d6659 Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Sun, 13 Oct 2019 17:02:25 -0700 Subject: [PATCH 502/547] This should make gox a little nicer --- travis/cross_build.sh | 2 +- travis/install.sh | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/travis/cross_build.sh b/travis/cross_build.sh index 1ba63ab38..824b4ee6e 100755 --- a/travis/cross_build.sh +++ b/travis/cross_build.sh @@ -1,5 +1,5 @@ #!/bin/bash if [[ "$TRAVIS_GO_VERSION" =~ ^1\.12\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - /tmp/gox/gox -build-lib -all + $(go env GOPATH)/bin/gox -build-lib fi diff --git a/travis/install.sh b/travis/install.sh index eaa511a06..70aa0a47d 100755 --- a/travis/install.sh +++ b/travis/install.sh @@ -3,11 +3,7 @@ set -e if [[ "$TRAVIS_GO_VERSION" =~ ^1\.12\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - git clone https://github.com/dgsb/gox.git /tmp/gox - pushd /tmp/gox - git checkout new_master - go build ./ - popd + GO111MODULE=off go get github.com/dgsb/gox fi if [[ "$GO111MODULE" == "on" ]]; then @@ -16,5 +12,4 @@ fi if [[ "$GO111MODULE" == "off" ]]; then go get github.com/stretchr/testify/assert golang.org/x/sys/unix github.com/konsorten/go-windows-terminal-sequences - go get github.com/hashicorp/go-version github.com/hashicorp/go-version fi From c7bb2cbfd79a53300a10ea8c4825fbfbcdc381ac Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Sun, 13 Oct 2019 17:25:32 -0700 Subject: [PATCH 503/547] Exclude go1.13.x from modules off, only build all on go1.13 modules on --- .travis.yml | 4 ++++ travis/cross_build.sh | 2 +- travis/install.sh | 6 ++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8e2407065..8bfc4f358 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,10 @@ env: - GO111MODULE=off go: [1.11.x, 1.12.x, 1.13.x] os: [linux, osx] +matrix: + exclude: + - go: 1.13.x + env: GO111MODULE=off ## Modules are the default now. install: - ./travis/install.sh script: diff --git a/travis/cross_build.sh b/travis/cross_build.sh index 824b4ee6e..5254435ca 100755 --- a/travis/cross_build.sh +++ b/travis/cross_build.sh @@ -1,5 +1,5 @@ #!/bin/bash -if [[ "$TRAVIS_GO_VERSION" =~ ^1\.12\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then +if [[ "$TRAVIS_GO_VERSION" =~ ^1\.13\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$GO111MODULE" == "on" ]]; then $(go env GOPATH)/bin/gox -build-lib fi diff --git a/travis/install.sh b/travis/install.sh index 70aa0a47d..127ac8b71 100755 --- a/travis/install.sh +++ b/travis/install.sh @@ -2,7 +2,8 @@ set -e -if [[ "$TRAVIS_GO_VERSION" =~ ^1\.12\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then +# Only do this for go1.12 when modules are on so that it doesn't need to be done when modules are off as well. +if [[ "$TRAVIS_GO_VERSION" =~ ^1\.13\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$GO111MODULE" == "on" ]]; then GO111MODULE=off go get github.com/dgsb/gox fi @@ -11,5 +12,6 @@ if [[ "$GO111MODULE" == "on" ]]; then fi if [[ "$GO111MODULE" == "off" ]]; then - go get github.com/stretchr/testify/assert golang.org/x/sys/unix github.com/konsorten/go-windows-terminal-sequences + # Should contain all regular (not indirect) modules from go.mod + go get github.com/stretchr/testify golang.org/x/sys/unix github.com/konsorten/go-windows-terminal-sequences fi From 213f203741989174ab14b1f47f31f47783b4ff04 Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Sun, 13 Oct 2019 17:41:41 -0700 Subject: [PATCH 504/547] Associate this example with what it's an example for --- example_custom_caller_test.go | 2 +- example_default_field_value_test.go | 5 +++-- example_global_hook_test.go | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/example_custom_caller_test.go b/example_custom_caller_test.go index e0023b9ad..a64ab1641 100644 --- a/example_custom_caller_test.go +++ b/example_custom_caller_test.go @@ -9,7 +9,7 @@ import ( "github.com/sirupsen/logrus" ) -func ExampleCustomFormatter() { +func ExampleJSONFormatter_CallerPrettyfier() { l := logrus.New() l.SetReportCaller(true) l.Out = os.Stdout diff --git a/example_default_field_value_test.go b/example_default_field_value_test.go index a72ece44a..e7edd1a8a 100644 --- a/example_default_field_value_test.go +++ b/example_default_field_value_test.go @@ -1,8 +1,9 @@ package logrus_test import ( - "github.com/sirupsen/logrus" "os" + + "github.com/sirupsen/logrus" ) type DefaultFieldHook struct { @@ -18,7 +19,7 @@ func (h *DefaultFieldHook) Fire(e *logrus.Entry) error { return nil } -func ExampleDefaultField() { +func ExampleDefaultFieldHook() { l := logrus.New() l.Out = os.Stdout l.Formatter = &logrus.TextFormatter{DisableTimestamp: true, DisableColors: true} diff --git a/example_global_hook_test.go b/example_global_hook_test.go index aaf2f4b1f..ff7b2559f 100644 --- a/example_global_hook_test.go +++ b/example_global_hook_test.go @@ -1,8 +1,9 @@ package logrus_test import ( - "github.com/sirupsen/logrus" "os" + + "github.com/sirupsen/logrus" ) var ( @@ -21,7 +22,7 @@ func (h *GlobalHook) Fire(e *logrus.Entry) error { return nil } -func ExampleGlobalVariableHook() { +func ExampleGlobalHook() { l := logrus.New() l.Out = os.Stdout l.Formatter = &logrus.TextFormatter{DisableTimestamp: true, DisableColors: true} From ad7a716faf2b4cd0db9409c3b3c4d27f854b3869 Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Sun, 13 Oct 2019 17:45:01 -0700 Subject: [PATCH 505/547] test the error to ensure there isn't an unexpected error in the test --- hook_test.go | 4 +++- logger_test.go | 8 ++++++-- logrus_test.go | 7 +++++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/hook_test.go b/hook_test.go index 28f193df9..b5cf077b1 100644 --- a/hook_test.go +++ b/hook_test.go @@ -211,6 +211,8 @@ func TestHookFireOrder(t *testing.T) { h.Add(&HookCallFunc{F: func() { checkers = append(checkers, "second hook") }}) h.Add(&HookCallFunc{F: func() { checkers = append(checkers, "third hook") }}) - h.Fire(InfoLevel, &Entry{}) + if err := h.Fire(InfoLevel, &Entry{}); err != nil { + t.Error("unexpected error:", err) + } require.Equal(t, []string{"first hook", "second hook", "third hook"}, checkers) } diff --git a/logger_test.go b/logger_test.go index 50433e60c..f12a04e74 100644 --- a/logger_test.go +++ b/logger_test.go @@ -21,7 +21,9 @@ func TestFieldValueError(t *testing.T) { l.WithField("func", func() {}).Info("test") fmt.Println(buf.String()) var data map[string]interface{} - json.Unmarshal(buf.Bytes(), &data) + if err := json.Unmarshal(buf.Bytes(), &data); err != nil { + t.Error("unexpected error", err) + } _, ok := data[FieldKeyLogrusError] require.True(t, ok) } @@ -37,7 +39,9 @@ func TestNoFieldValueError(t *testing.T) { l.WithField("str", "str").Info("test") fmt.Println(buf.String()) var data map[string]interface{} - json.Unmarshal(buf.Bytes(), &data) + if err := json.Unmarshal(buf.Bytes(), &data); err != nil { + t.Error("unexpected error", err) + } _, ok := data[FieldKeyLogrusError] require.False(t, ok) } diff --git a/logrus_test.go b/logrus_test.go index 51f09a221..9c3c920de 100644 --- a/logrus_test.go +++ b/logrus_test.go @@ -656,11 +656,14 @@ func TestEntryWriter(t *testing.T) { log := New() log.Out = cw log.Formatter = new(JSONFormatter) - log.WithField("foo", "bar").WriterLevel(WarnLevel).Write([]byte("hello\n")) + _, err := log.WithField("foo", "bar").WriterLevel(WarnLevel).Write([]byte("hello\n")) + if err != nil { + t.Error("unexecpted error", err) + } bs := <-cw var fields Fields - err := json.Unmarshal(bs, &fields) + err = json.Unmarshal(bs, &fields) assert.Nil(t, err) assert.Equal(t, fields["foo"], "bar") assert.Equal(t, fields["level"], "warning") From dcec51351c702632c2c73800b65c3eb3342ea35f Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Sun, 13 Oct 2019 17:45:50 -0700 Subject: [PATCH 506/547] deadcode --- logger_bench_test.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/logger_bench_test.go b/logger_bench_test.go index f0a768439..1699af5aa 100644 --- a/logger_bench_test.go +++ b/logger_bench_test.go @@ -6,14 +6,6 @@ import ( "testing" ) -// smallFields is a small size data set for benchmarking -var loggerFields = Fields{ - "foo": "bar", - "baz": "qux", - "one": "two", - "three": "four", -} - func BenchmarkDummyLogger(b *testing.B) { nullf, err := os.OpenFile("/dev/null", os.O_WRONLY, 0666) if err != nil { From b8701320fe4733693e8dce14b06bb6186c740805 Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Mon, 14 Oct 2019 13:23:44 -0700 Subject: [PATCH 507/547] fix broken test --- example_custom_caller_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example_custom_caller_test.go b/example_custom_caller_test.go index a64ab1641..6749effbc 100644 --- a/example_custom_caller_test.go +++ b/example_custom_caller_test.go @@ -24,5 +24,5 @@ func ExampleJSONFormatter_CallerPrettyfier() { } l.Info("example of custom format caller") // Output: - // {"file":"example_custom_caller_test.go","func":"ExampleCustomFormatter","level":"info","msg":"example of custom format caller"} + // {"file":"example_custom_caller_test.go","func":"ExampleJSONFormatter_CallerPrettyfier","level":"info","msg":"example of custom format caller"} } From b8016f813735227fc59a17028d660e23345ba30c Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Mon, 14 Oct 2019 22:53:51 -0700 Subject: [PATCH 508/547] Force Quote Closed #1005 --- text_formatter.go | 6 ++++++ text_formatter_test.go | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/text_formatter.go b/text_formatter.go index f08563e45..5867b25e8 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -34,6 +34,9 @@ type TextFormatter struct { // Force disabling colors. DisableColors bool + // Force quoting of all values + ForceQuote bool + // Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/ EnvironmentOverrideColors bool @@ -283,6 +286,9 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin } func (f *TextFormatter) needsQuoting(text string) bool { + if f.ForceQuote { + return true + } if f.QuoteEmptyFields && len(text) == 0 { return true } diff --git a/text_formatter_test.go b/text_formatter_test.go index 99b5d8cde..5d94ebb76 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -71,6 +71,12 @@ func TestQuoting(t *testing.T) { checkQuoting(true, "") checkQuoting(false, "abcd") checkQuoting(true, errors.New("invalid argument")) + + // Test forcing quotes. + tf.ForceQuote = true + checkQuoting(true, "") + checkQuoting(true, "abcd") + checkQuoting(true, errors.New("invalid argument")) } func TestEscaping(t *testing.T) { From 2b88d5b04d018c0a8f88a21c22e2b8395cbeed47 Mon Sep 17 00:00:00 2001 From: lwsanty Date: Wed, 23 Oct 2019 20:43:07 +0300 Subject: [PATCH 509/547] fix race conditions on entry closes #1046 --- entry.go | 2 ++ entry_test.go | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/entry.go b/entry.go index 7e2c8993a..b3a808263 100644 --- a/entry.go +++ b/entry.go @@ -113,6 +113,8 @@ func (entry *Entry) WithField(key string, value interface{}) *Entry { // Add a map of fields to the Entry. func (entry *Entry) WithFields(fields Fields) *Entry { + entry.Logger.mu.Lock() + defer entry.Logger.mu.Unlock() data := make(Fields, len(entry.Data)+len(fields)) for k, v := range entry.Data { data[k] = v diff --git a/entry_test.go b/entry_test.go index f764085ef..e1cb1b5a0 100644 --- a/entry_test.go +++ b/entry_test.go @@ -134,7 +134,7 @@ func TestEntryWithIncorrectField(t *testing.T) { fn := func() {} - e := Entry{} + e := &Entry{Logger: New()} eWithFunc := e.WithFields(Fields{"func": fn}) eWithFuncPtr := e.WithFields(Fields{"funcPtr": &fn}) @@ -162,8 +162,8 @@ func TestEntryLogfLevel(t *testing.T) { entry := NewEntry(logger) entry.Logf(DebugLevel, "%s", "debug") - assert.NotContains(t, buffer.String(), "debug", ) + assert.NotContains(t, buffer.String(), "debug") entry.Logf(WarnLevel, "%s", "warn") - assert.Contains(t, buffer.String(), "warn", ) -} \ No newline at end of file + assert.Contains(t, buffer.String(), "warn") +} From 5d9c54c8f257b884c90e6f4718665f55e80b06d0 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Fri, 25 Oct 2019 14:49:48 +0200 Subject: [PATCH 510/547] run golangci-lint on travis --- .golangci.yml | 40 ++++++++++++++++++++++++++++++++++++++++ .travis.yml | 1 + entry.go | 2 +- travis/install.sh | 5 +++++ travis/lint.sh | 5 +++++ 5 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 .golangci.yml create mode 100644 travis/lint.sh diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 000000000..65dc28503 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,40 @@ +run: + # do not run on test files yet + tests: false + +# all available settings of specific linters +linters-settings: + errcheck: + # report about not checking of errors in type assetions: `a := b.(MyStruct)`; + # default is false: such cases aren't reported by default. + check-type-assertions: false + + # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; + # default is false: such cases aren't reported by default. + check-blank: false + + lll: + line-length: 100 + tab-width: 4 + + prealloc: + simple: false + range-loops: false + for-loops: false + + whitespace: + multi-if: false # Enforces newlines (or comments) after every multi-line if statement + multi-func: false # Enforces newlines (or comments) after every multi-line function signature + +linters: + enable: + - megacheck + - govet + disable: + - maligned + - prealloc + disable-all: false + presets: + - bugs + - unused + fast: false diff --git a/.travis.yml b/.travis.yml index 8bfc4f358..06918c995 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,7 @@ install: - ./travis/install.sh script: - ./travis/cross_build.sh + - ./travis/lint.sh - export GOMAXPROCS=4 - export GORACE=halt_on_error=1 - go test -race -v ./... diff --git a/entry.go b/entry.go index 7e2c8993a..f24cf8d7d 100644 --- a/entry.go +++ b/entry.go @@ -187,7 +187,7 @@ func getCaller() *runtime.Frame { // If the caller isn't part of this package, we're done if pkg != logrusPackage { - return &f + return &f //nolint:scopelint } } diff --git a/travis/install.sh b/travis/install.sh index 127ac8b71..6fa948646 100755 --- a/travis/install.sh +++ b/travis/install.sh @@ -2,6 +2,11 @@ set -e +# Install golanci 1.21.0 +if [[ "$TRAVIS_GO_VERSION" =~ ^1\.13\. ]]; then + curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.21.0 +fi + # Only do this for go1.12 when modules are on so that it doesn't need to be done when modules are off as well. if [[ "$TRAVIS_GO_VERSION" =~ ^1\.13\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$GO111MODULE" == "on" ]]; then GO111MODULE=off go get github.com/dgsb/gox diff --git a/travis/lint.sh b/travis/lint.sh new file mode 100644 index 000000000..0ed1d7c74 --- /dev/null +++ b/travis/lint.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +if [[ "$TRAVIS_GO_VERSION" =~ ^1\.13\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$GO111MODULE" == "on" ]]; then + $(go env GOPATH)/bin/golangci-lint run ./... +fi From 0908a2726f22d9feeb7ba7434f4679a38b709528 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Fri, 25 Oct 2019 14:53:19 +0200 Subject: [PATCH 511/547] remove go1.11.x from travis build matrix --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8bfc4f358..cd000d816 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ git: env: - GO111MODULE=on - GO111MODULE=off -go: [1.11.x, 1.12.x, 1.13.x] +go: [1.12.x, 1.13.x] os: [linux, osx] matrix: exclude: From d4a89bdccde22431df8f639248f30e4c6bbcda1a Mon Sep 17 00:00:00 2001 From: David Bariod Date: Fri, 25 Oct 2019 14:57:57 +0200 Subject: [PATCH 512/547] add x rights on travis/lint.sh --- travis/lint.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 travis/lint.sh diff --git a/travis/lint.sh b/travis/lint.sh old mode 100644 new mode 100755 From 6568f70c426860c2231bdada4473b4030f1225e4 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sat, 26 Oct 2019 08:50:02 +0200 Subject: [PATCH 513/547] remove obsolete documentation --- hooks/test/test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hooks/test/test.go b/hooks/test/test.go index 234a17dfa..b16d06654 100644 --- a/hooks/test/test.go +++ b/hooks/test/test.go @@ -1,6 +1,5 @@ -// 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. +// The Test package is used for testing logrus. +// It provides a simple hooks which register logged messages. package test import ( From e695648ce73a71e64e31fd1f3b8f21dfd2eb50fd Mon Sep 17 00:00:00 2001 From: David Bariod Date: Mon, 28 Oct 2019 19:22:23 +0100 Subject: [PATCH 514/547] improve Logger.WithField documentation --- logger.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/logger.go b/logger.go index c0c0b1e55..b827e4499 100644 --- a/logger.go +++ b/logger.go @@ -100,8 +100,9 @@ func (logger *Logger) releaseEntry(entry *Entry) { logger.entryPool.Put(entry) } -// Adds a field to the log entry, note that it doesn't log until you call -// Debug, Print, Info, Warn, Error, Fatal or Panic. It only creates a log entry. +// WithField allocates a new entry and adds a field to it. +// Debug, Print, Info, Warn, Error, Fatal or Panic must be then applied to +// this new returned entry. // If you want multiple fields, use `WithFields`. func (logger *Logger) WithField(key string, value interface{}) *Entry { entry := logger.newEntry() From 8b47487f0b9438c02f0c6876df31e40dc37a7fae Mon Sep 17 00:00:00 2001 From: Taylor Wrobel Date: Wed, 27 Nov 2019 20:03:38 -0800 Subject: [PATCH 515/547] Fix entity data bleed when using WithContext and WithTime Creates a copy of the data map when using WithContext to create a child entity. Without this, the data map of the parent entitiy, which is exposed in the entity struct, is shared between a parent and all children instances. This can create bugs of shared or overwritten data when a parent entity is used to make children in differing contexts, and behaves differently than `WithField` and its diritivites which does make a copy of the data. Additionally implements the same logic for WithTime, for API consistency in behavior. --- entry.go | 12 ++++++++++-- entry_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/entry.go b/entry.go index f24cf8d7d..7f28ba48e 100644 --- a/entry.go +++ b/entry.go @@ -103,7 +103,11 @@ func (entry *Entry) WithError(err error) *Entry { // Add a context to the Entry. func (entry *Entry) WithContext(ctx context.Context) *Entry { - return &Entry{Logger: entry.Logger, Data: entry.Data, Time: entry.Time, err: entry.err, Context: ctx} + dataCopy := make(Fields, len(entry.Data)) + for k, v := range entry.Data { + dataCopy[k] = v + } + return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx} } // Add a single field to the Entry. @@ -144,7 +148,11 @@ func (entry *Entry) WithFields(fields Fields) *Entry { // Overrides the time of the Entry. func (entry *Entry) WithTime(t time.Time) *Entry { - return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t, err: entry.err, Context: entry.Context} + dataCopy := make(Fields, len(entry.Data)) + for k, v := range entry.Data { + dataCopy[k] = v + } + return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context} } // getPackageName reduces a fully qualified function name to the package name diff --git a/entry_test.go b/entry_test.go index f764085ef..7be48aa04 100644 --- a/entry_test.go +++ b/entry_test.go @@ -47,6 +47,34 @@ func TestEntryWithContext(t *testing.T) { assert.Equal(ctx, entry.WithContext(ctx).Context) } +func TestEntryWithContextCopiesData(t *testing.T) { + assert := assert.New(t) + ctx1 := context.WithValue(context.Background(), "foo", "bar") + ctx2 := context.WithValue(context.Background(), "bar", "baz") + assert.NotEqual(ctx1, ctx2) + + logger := New() + logger.Out = &bytes.Buffer{} + parentEntry := NewEntry(logger).WithField("parentKey", "parentValue") + childEntry1 := parentEntry.WithContext(ctx1) + assert.Equal(ctx1, childEntry1.Context) + childEntry2 := parentEntry.WithContext(ctx2) + assert.Equal(ctx2, childEntry2.Context) + assert.NotEqual(ctx1, ctx2) + assert.Equal("parentValue", childEntry1.Data["parentKey"]) + assert.Equal("parentValue", childEntry2.Data["parentKey"]) + + childEntry1.Data["ChildKeyValue1"] = "ChildDataValue1" + + val, exists := childEntry1.Data["ChildKeyValue1"] + assert.True(exists) + assert.Equal("ChildDataValue1", val) + + val, exists = childEntry2.Data["ChildKeyValue1"] + assert.False(exists) + assert.Empty(val) +} + func TestEntryPanicln(t *testing.T) { errBoom := fmt.Errorf("boom time") From 5b1edf72bda86cdb6d21140d33d463957631c69c Mon Sep 17 00:00:00 2001 From: Taylor Wrobel Date: Tue, 3 Dec 2019 13:50:59 -0800 Subject: [PATCH 516/547] Make Entry WithContext and WithTime copy tests more clear Clarifies the data used in the EntryWithContextCopiesData test and adds an equivalent test to verify the behavior of WithTime. --- entry_test.go | 62 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/entry_test.go b/entry_test.go index 7be48aa04..973091e68 100644 --- a/entry_test.go +++ b/entry_test.go @@ -49,28 +49,76 @@ func TestEntryWithContext(t *testing.T) { func TestEntryWithContextCopiesData(t *testing.T) { assert := assert.New(t) - ctx1 := context.WithValue(context.Background(), "foo", "bar") - ctx2 := context.WithValue(context.Background(), "bar", "baz") - assert.NotEqual(ctx1, ctx2) + // Initialize a parent Entry object with a key/value set in its Data map logger := New() logger.Out = &bytes.Buffer{} parentEntry := NewEntry(logger).WithField("parentKey", "parentValue") + + // Create two children Entry objects from the parent in different contexts + ctx1 := context.WithValue(context.Background(), "foo", "bar") childEntry1 := parentEntry.WithContext(ctx1) assert.Equal(ctx1, childEntry1.Context) + + ctx2 := context.WithValue(context.Background(), "bar", "baz") childEntry2 := parentEntry.WithContext(ctx2) assert.Equal(ctx2, childEntry2.Context) assert.NotEqual(ctx1, ctx2) + + // Ensure that data set in the parent Entry are preserved to both children assert.Equal("parentValue", childEntry1.Data["parentKey"]) assert.Equal("parentValue", childEntry2.Data["parentKey"]) - childEntry1.Data["ChildKeyValue1"] = "ChildDataValue1" + // Modify data stored in the child entry + childEntry1.Data["childKey"] = "childValue" - val, exists := childEntry1.Data["ChildKeyValue1"] + // Verify that data is successfully stored in the child it was set on + val, exists := childEntry1.Data["childKey"] assert.True(exists) - assert.Equal("ChildDataValue1", val) + assert.Equal("childValue", val) + + // Verify that the data change to child 1 has not affected its sibling + val, exists = childEntry2.Data["childKey"] + assert.False(exists) + assert.Empty(val) + + // Verify that the data change to child 1 has not affected its parent + val, exists = parentEntry.Data["childKey"] + assert.False(exists) + assert.Empty(val) +} + +func TestEntryWithTimeCopiesData(t *testing.T) { + assert := assert.New(t) + + // Initialize a parent Entry object with a key/value set in its Data map + logger := New() + logger.Out = &bytes.Buffer{} + parentEntry := NewEntry(logger).WithField("parentKey", "parentValue") + + // Create two children Entry objects from the parent with two different times + childEntry1 := parentEntry.WithTime(time.Now().AddDate(0, 0, 1)) + childEntry2 := parentEntry.WithTime(time.Now().AddDate(0, 0, 2)) + + // Ensure that data set in the parent Entry are preserved to both children + assert.Equal("parentValue", childEntry1.Data["parentKey"]) + assert.Equal("parentValue", childEntry2.Data["parentKey"]) + + // Modify data stored in the child entry + childEntry1.Data["childKey"] = "childValue" + + // Verify that data is successfully stored in the child it was set on + val, exists := childEntry1.Data["childKey"] + assert.True(exists) + assert.Equal("childValue", val) + + // Verify that the data change to child 1 has not affected its sibling + val, exists = childEntry2.Data["childKey"] + assert.False(exists) + assert.Empty(val) - val, exists = childEntry2.Data["ChildKeyValue1"] + // Verify that the data change to child 1 has not affected its parent + val, exists = parentEntry.Data["childKey"] assert.False(exists) assert.Empty(val) } From 3e78f9824906a6f892c3c9a26af90a39156c784a Mon Sep 17 00:00:00 2001 From: Dmitri Goutnik Date: Mon, 9 Dec 2019 07:44:42 -0500 Subject: [PATCH 517/547] Add support for freebsd/arm64 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 9ea6e841b..402198aea 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ require ( github.com/konsorten/go-windows-terminal-sequences v1.0.1 github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 - golang.org/x/sys v0.0.0-20190422165155-953cdadca894 + golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 ) go 1.13 diff --git a/go.sum b/go.sum index 95a3f07de..4f70b43eb 100644 --- a/go.sum +++ b/go.sum @@ -8,3 +8,5 @@ github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= From f698bb4d393cfad75281001e02d782ad4e8d5817 Mon Sep 17 00:00:00 2001 From: Alex Shi Date: Wed, 18 Dec 2019 14:10:06 +0800 Subject: [PATCH 518/547] Fix typo --- exported.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exported.go b/exported.go index 62fc2f219..42b04f6c8 100644 --- a/exported.go +++ b/exported.go @@ -80,7 +80,7 @@ func WithFields(fields Fields) *Entry { return std.WithFields(fields) } -// WithTime creats an entry from the standard logger and overrides the time of +// WithTime creates an entry from the standard logger and overrides the time of // logs generated with it. // // Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal From c6e99be3ff57ca4c099eab8f959e09ff8e25e3ff Mon Sep 17 00:00:00 2001 From: nolleh Date: Tue, 21 Jan 2020 00:42:07 +0900 Subject: [PATCH 519/547] add caption-json-formatter --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b475e4a58..948a9b92f 100644 --- a/README.md +++ b/README.md @@ -363,6 +363,7 @@ Third party logging formatters: * [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. * [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure. * [`powerful-logrus-formatter`](https://github.com/zput/zxcTool). get fileName, log's line number and the latest function's name when print log; Sava log to files. +* [`caption-json-formatter`](https://github.com/nolleh/caption_json_formatter). logrus's message json formatter with human-readable caption added. You can define your formatter by implementing the `Formatter` interface, requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a From 7c43d5b7e0434c1126eea4401ea3899e7b0a1672 Mon Sep 17 00:00:00 2001 From: Mikolaj Holysz Date: Tue, 28 Jan 2020 19:39:15 +0100 Subject: [PATCH 520/547] Remove annoying punctuation in Readme for better screen reader accessibility. One entry in the Logrus formatters list in the readme contained a lot of extraneous punctuation. When read with a screen reader, nothing but a bunch of question marks and weird symbol names could be heard, making the line impossible to understand. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b475e4a58..cd405977c 100644 --- a/README.md +++ b/README.md @@ -360,7 +360,7 @@ Third party logging formatters: * [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html). * [`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̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. +* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the Power of Zalgo. * [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure. * [`powerful-logrus-formatter`](https://github.com/zput/zxcTool). get fileName, log's line number and the latest function's name when print log; Sava log to files. From 5ac800719e4dce1251caafa28376868186a84395 Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Tue, 25 Feb 2020 07:39:50 -0500 Subject: [PATCH 521/547] readme: maintenance-mode --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index cd405977c..7479048a5 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,26 @@ Logrus is a structured logger for Go (golang), completely API compatible with the standard library logger. +**Logrus is in maintenance-mode.** We will not be introducing new features. It's +simply too hard to do in a way that won't break many people's projects, which is +the last thing you want from your Logging library (again...). + +This does not mean Logrus is dead. Logrus will continue to be maintained for +security, (backwards compatible) bug fixes, and performance (where we are +limited by the interface). + +I believe Logrus' biggest contribution is to have played a part in today's +widespread use of structured logging in Golang. There doesn't seem to be a +reason to do a major, breaking iteration into Logrus V2, since the fantastic Go +community has built those independently. Many fantastic alternatives have sprung +up. Logrus would look like those, had it been re-designed with what we know +about structured logging in Go today. Check out, for example, +[Zerolog][zerolog], [Zap][zap], and [Apex][apex]. + +[zerolog]: https://github.com/rs/zerolog +[zap]: https://github.com/uber-go/zap +[apex]: https://github.com/apex/log + **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 From 0d4dc2f1d8a5b9be793ce6f37116f8c396673c84 Mon Sep 17 00:00:00 2001 From: Mark Phelps Date: Wed, 26 Feb 2020 11:58:33 -0500 Subject: [PATCH 522/547] Create stale.yml --- .github/stale.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000..1273af7f9 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,18 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 30 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 14 +# Issues with these labels will never be considered stale +exemptLabels: + - question + - bug + - documentation +# Label to use when marking an issue as stale +staleLabel: stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false From e685ebc886b97dd3b421a04466815076fa9bc030 Mon Sep 17 00:00:00 2001 From: Mark Phelps Date: Wed, 26 Feb 2020 12:01:50 -0500 Subject: [PATCH 523/547] Update stale.yml --- .github/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/stale.yml b/.github/stale.yml index 1273af7f9..65a41e30b 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,5 +1,5 @@ # Number of days of inactivity before an issue becomes stale -daysUntilStale: 30 +daysUntilStale: 60 # Number of days of inactivity before a stale issue is closed daysUntilClose: 14 # Issues with these labels will never be considered stale From f5c36a291b9f0d70e01225e9407e53a2e2e408b1 Mon Sep 17 00:00:00 2001 From: Mark Phelps Date: Wed, 26 Feb 2020 18:18:33 -0500 Subject: [PATCH 524/547] Only mark issues as stale for now until we go through backlog of PRs Only mark issues as stale for now until we go through backlog of PRs --- .github/stale.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/stale.yml b/.github/stale.yml index 65a41e30b..27c3cf9d4 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -16,3 +16,5 @@ markComment: > for your contributions. # Comment to post when closing a stale issue. Set to `false` to disable closeComment: false +# Only mark issues as stale for now until we go through backlog of PRs +only: issues From 284cce4e2f25e2d8d90892f41f302071a4642ae4 Mon Sep 17 00:00:00 2001 From: Alex S Date: Thu, 27 Feb 2020 11:04:36 +0800 Subject: [PATCH 525/547] Get right logrus package name --- entry.go | 2 +- versions_go1_14.go | 10 ++++++++++ versions_others.go | 10 ++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 versions_go1_14.go create mode 100644 versions_others.go diff --git a/entry.go b/entry.go index 5c971f69b..1bf127c3b 100644 --- a/entry.go +++ b/entry.go @@ -185,7 +185,7 @@ func getCaller() *runtime.Frame { callerInitOnce.Do(func() { pcs := make([]uintptr, 2) _ = runtime.Callers(0, pcs) - logrusPackage = getPackageName(runtime.FuncForPC(pcs[1]).Name()) + logrusPackage = getPackageName(funcName(pcs)) // now that we have the cache, we can skip a minimum count of known-logrus functions // XXX this is dubious, the number of frames may vary diff --git a/versions_go1_14.go b/versions_go1_14.go new file mode 100644 index 000000000..8db33606a --- /dev/null +++ b/versions_go1_14.go @@ -0,0 +1,10 @@ +// +build go1.14 + +package logrus + +import "runtime" + +// funcName returns the function name that logrus calls +func funcName(pcs []uintptr) string { + return runtime.FuncForPC(pcs[0]).Name() +} diff --git a/versions_others.go b/versions_others.go new file mode 100644 index 000000000..8b3a68204 --- /dev/null +++ b/versions_others.go @@ -0,0 +1,10 @@ +// +build !go1.14 + +package logrus + +import "runtime" + +// funcName returns the function name that logrus calls +func funcName(pcs []uintptr) string { + return runtime.FuncForPC(pcs[1]).Name() +} From 6a48e94fec789c1f0d9a8e3d385939c4658bc6e9 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Tue, 3 Mar 2020 09:10:14 +0100 Subject: [PATCH 526/547] run CI for go 1.13 and 1.14 --- .travis.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 51470a3b4..5e20aa414 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,13 +4,8 @@ git: depth: 1 env: - GO111MODULE=on - - GO111MODULE=off -go: [1.12.x, 1.13.x] +go: [1.13.x, 1.14.x] os: [linux, osx] -matrix: - exclude: - - go: 1.13.x - env: GO111MODULE=off ## Modules are the default now. install: - ./travis/install.sh script: From cce21019d6ccec35f51624ed2ca2532637dc8978 Mon Sep 17 00:00:00 2001 From: Fabrizio Cirelli Date: Mon, 9 Mar 2020 12:36:52 +0100 Subject: [PATCH 527/547] Fix wrong caller --- entry.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/entry.go b/entry.go index 1bf127c3b..defb8bc66 100644 --- a/entry.go +++ b/entry.go @@ -180,15 +180,20 @@ func getPackageName(f string) string { // getCaller retrieves the name of the first non-logrus calling function func getCaller() *runtime.Frame { - // cache this package's fully-qualified name callerInitOnce.Do(func() { - pcs := make([]uintptr, 2) + pcs := make([]uintptr, maximumCallerDepth) _ = runtime.Callers(0, pcs) - logrusPackage = getPackageName(funcName(pcs)) - // now that we have the cache, we can skip a minimum count of known-logrus functions - // XXX this is dubious, the number of frames may vary + // dynamic get the package name and the minimum caller depth + for i := 0; i < maximumCallerDepth; i++ { + funcName := runtime.FuncForPC(pcs[i]).Name() + if strings.Contains(funcName, "getCaller") { + logrusPackage = getPackageName(funcName) + break + } + } + minimumCallerDepth = knownLogrusFrames }) From 6b08d78698a51065a79dbe09183cb6672968766b Mon Sep 17 00:00:00 2001 From: Fabrizio Cirelli Date: Mon, 9 Mar 2020 14:45:58 +0100 Subject: [PATCH 528/547] Removed useless files --- versions_go1_14.go | 10 ---------- versions_others.go | 10 ---------- 2 files changed, 20 deletions(-) delete mode 100644 versions_go1_14.go delete mode 100644 versions_others.go diff --git a/versions_go1_14.go b/versions_go1_14.go deleted file mode 100644 index 8db33606a..000000000 --- a/versions_go1_14.go +++ /dev/null @@ -1,10 +0,0 @@ -// +build go1.14 - -package logrus - -import "runtime" - -// funcName returns the function name that logrus calls -func funcName(pcs []uintptr) string { - return runtime.FuncForPC(pcs[0]).Name() -} diff --git a/versions_others.go b/versions_others.go deleted file mode 100644 index 8b3a68204..000000000 --- a/versions_others.go +++ /dev/null @@ -1,10 +0,0 @@ -// +build !go1.14 - -package logrus - -import "runtime" - -// funcName returns the function name that logrus calls -func funcName(pcs []uintptr) string { - return runtime.FuncForPC(pcs[1]).Name() -} From a700adb281b0120f583e7e5f1c032b2c9d68c8bb Mon Sep 17 00:00:00 2001 From: Deep Datta Date: Wed, 11 Mar 2020 17:26:45 -0700 Subject: [PATCH 529/547] Title updates Removed the non-breaking spaces in the ReadMe --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e958acd18..5796706db 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) Logrus is a structured logger for Go (golang), completely API compatible with the standard library logger. From b06b205e90db754a5dd99af3adc75e3e9b091d0a Mon Sep 17 00:00:00 2001 From: David Raleigh Date: Fri, 13 Mar 2020 13:22:12 -0400 Subject: [PATCH 530/547] resolved conflicts --- json_formatter.go | 4 ++++ json_formatter_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/json_formatter.go b/json_formatter.go index 098a21a06..ba7f23711 100644 --- a/json_formatter.go +++ b/json_formatter.go @@ -28,6 +28,9 @@ type JSONFormatter struct { // DisableTimestamp allows disabling automatic timestamps in output DisableTimestamp bool + // DisableHTMLEscape allows disabling html escaping in output + DisableHTMLEscape bool + // DataKey allows users to put all the log entry parameters into a nested dictionary at a given key. DataKey string @@ -110,6 +113,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { } encoder := json.NewEncoder(b) + encoder.SetEscapeHTML(!f.DisableHTMLEscape) if f.PrettyPrint { encoder.SetIndent("", " ") } diff --git a/json_formatter_test.go b/json_formatter_test.go index 695c36e54..7a48f2dc6 100644 --- a/json_formatter_test.go +++ b/json_formatter_test.go @@ -344,3 +344,29 @@ func TestJSONEnableTimestamp(t *testing.T) { t.Error("Timestamp not present", s) } } + +func TestJSONDisableHTMLEscape(t *testing.T) { + formatter := &JSONFormatter{DisableHTMLEscape: true} + + b, err := formatter.Format(&Entry{Message: "& < >"}) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + s := string(b) + if !strings.Contains(s, "& < >") { + t.Error("Message should not be HTML escaped", s) + } +} + +func TestJSONEnableHTMLEscape(t *testing.T) { + formatter := &JSONFormatter{} + + b, err := formatter.Format(&Entry{Message: "& < >"}) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + s := string(b) + if !(strings.Contains(s, "u0026") && strings.Contains(s, "u003e") && strings.Contains(s, "u003c")) { + t.Error("Message should be HTML escaped", s) + } +} From 013c86553cc9beb923a3c34b40de2a82bb8ad949 Mon Sep 17 00:00:00 2001 From: Alisdair MacLeod Date: Thu, 19 Mar 2020 09:29:19 +0000 Subject: [PATCH 531/547] create test to prove issue sirupsen/logrus#954 --- entry_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/entry_test.go b/entry_test.go index 423e5ed9c..30eddaecc 100644 --- a/entry_test.go +++ b/entry_test.go @@ -243,3 +243,14 @@ func TestEntryLogfLevel(t *testing.T) { entry.Logf(WarnLevel, "%s", "warn") assert.Contains(t, buffer.String(), "warn") } + +func TestEntryReportCallerRace(t *testing.T) { + logger := New() + entry := NewEntry(logger) + go func() { + logger.SetReportCaller(true) + }() + go func() { + entry.Info("should not race") + }() +} From 9b298a80208f4c27aba5a207dabd82129a9421d8 Mon Sep 17 00:00:00 2001 From: Alisdair MacLeod Date: Thu, 19 Mar 2020 09:32:08 +0000 Subject: [PATCH 532/547] fix race condition in entry --- entry.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/entry.go b/entry.go index 1bf127c3b..3928671eb 100644 --- a/entry.go +++ b/entry.go @@ -219,6 +219,8 @@ func (entry Entry) HasCaller() (has bool) { // This function is not declared with a pointer value because otherwise // race conditions will occur when using multiple goroutines func (entry Entry) log(level Level, msg string) { + entry.Logger.mu.Lock() + defer entry.Logger.mu.Unlock() var buffer *bytes.Buffer // Default to now, but allow users to override if they want. From 5cf0721be7be721452b0bd6cb286e5d9fab3654c Mon Sep 17 00:00:00 2001 From: Alisdair MacLeod Date: Thu, 19 Mar 2020 10:01:29 +0000 Subject: [PATCH 533/547] fix deadlock in previous entry race condition fix --- entry.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/entry.go b/entry.go index 3928671eb..5082cfa3d 100644 --- a/entry.go +++ b/entry.go @@ -219,8 +219,7 @@ func (entry Entry) HasCaller() (has bool) { // This function is not declared with a pointer value because otherwise // race conditions will occur when using multiple goroutines func (entry Entry) log(level Level, msg string) { - entry.Logger.mu.Lock() - defer entry.Logger.mu.Unlock() + var buffer *bytes.Buffer // Default to now, but allow users to override if they want. @@ -234,9 +233,11 @@ func (entry Entry) log(level Level, msg string) { entry.Level = level entry.Message = msg + entry.Logger.mu.Lock() if entry.Logger.ReportCaller { entry.Caller = getCaller() } + entry.Logger.mu.Unlock() entry.fireHooks() From c41966647f7803b22aa7feffdbcc950dd83ccaea Mon Sep 17 00:00:00 2001 From: Alisdair MacLeod Date: Thu, 19 Mar 2020 10:02:20 +0000 Subject: [PATCH 534/547] remove errant whitespace --- entry.go | 1 - 1 file changed, 1 deletion(-) diff --git a/entry.go b/entry.go index 5082cfa3d..c6da56291 100644 --- a/entry.go +++ b/entry.go @@ -219,7 +219,6 @@ func (entry Entry) HasCaller() (has bool) { // This function is not declared with a pointer value because otherwise // race conditions will occur when using multiple goroutines func (entry Entry) log(level Level, msg string) { - var buffer *bytes.Buffer // Default to now, but allow users to override if they want. From 41bb196b95034184ef1a2efb1e25ad4c9a66b449 Mon Sep 17 00:00:00 2001 From: Ariel Simulevski Date: Fri, 10 Apr 2020 12:42:19 +0200 Subject: [PATCH 535/547] Add loggers that take functions as input --- .gitignore | 2 ++ example_function_test.go | 26 ++++++++++++++++++++ exported.go | 45 ++++++++++++++++++++++++++++++++++ logger.go | 52 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 example_function_test.go diff --git a/.gitignore b/.gitignore index 6b7d7d1e8..1fb13abeb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ logrus vendor + +.idea/ diff --git a/example_function_test.go b/example_function_test.go new file mode 100644 index 000000000..f61460eca --- /dev/null +++ b/example_function_test.go @@ -0,0 +1,26 @@ +package logrus_test + +import ( + "fmt" + log "github.com/sirupsen/logrus" + "testing" +) + +func TestLogger_LogFn(t *testing.T) { + log.SetFormatter(&log.JSONFormatter{}) + log.SetLevel(log.WarnLevel) + + log.InfoFn(func() []interface{} { + fmt.Println("This is never run") + return []interface{} { + "Hello", + } + }) + + log.ErrorFn(func() []interface{} { + fmt.Println("This runs") + return []interface{} { + "Oopsi", + } + }) +} diff --git a/exported.go b/exported.go index 42b04f6c8..017c30ce6 100644 --- a/exported.go +++ b/exported.go @@ -134,6 +134,51 @@ func Fatal(args ...interface{}) { std.Fatal(args...) } +// TraceFn logs a message from a func at level Trace on the standard logger. +func TraceFn(fn LogFunction) { + std.TraceFn(fn) +} + +// DebugFn logs a message from a func at level Debug on the standard logger. +func DebugFn(fn LogFunction) { + std.DebugFn(fn) +} + +// PrintFn logs a message from a func at level Info on the standard logger. +func PrintFn(fn LogFunction) { + std.PrintFn(fn) +} + +// InfoFn logs a message from a func at level Info on the standard logger. +func InfoFn(fn LogFunction) { + std.InfoFn(fn) +} + +// WarnFn logs a message from a func at level Warn on the standard logger. +func WarnFn(fn LogFunction) { + std.WarnFn(fn) +} + +// WarningFn logs a message from a func at level Warn on the standard logger. +func WarningFn(fn LogFunction) { + std.WarningFn(fn) +} + +// ErrorFn logs a message from a func at level Error on the standard logger. +func ErrorFn(fn LogFunction) { + std.ErrorFn(fn) +} + +// PanicFn logs a message from a func at level Panic on the standard logger. +func PanicFn(fn LogFunction) { + std.PanicFn(fn) +} + +// FatalFn logs a message from a func at level Fatal on the standard logger then the process will exit with status set to 1. +func FatalFn(fn LogFunction) { + std.FatalFn(fn) +} + // Tracef logs a message at level Trace on the standard logger. func Tracef(format string, args ...interface{}) { std.Tracef(format, args...) diff --git a/logger.go b/logger.go index 6fdda748e..dae5e5708 100644 --- a/logger.go +++ b/logger.go @@ -9,6 +9,11 @@ import ( "time" ) +// LogFunction For big messages, it can be more efficient to pass a function +// and only call it if the log level is actually enables rather than +// generating the log message and then checking if the level is enabled +type LogFunction func()[]interface{} + type Logger struct { // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a // file, or leave it default which is `os.Stderr`. You can also set this to @@ -195,6 +200,14 @@ func (logger *Logger) Log(level Level, args ...interface{}) { } } +func (logger *Logger) LogFn(level Level, fn LogFunction) { + if logger.IsLevelEnabled(level) { + entry := logger.newEntry() + entry.Log(level, fn()...) + logger.releaseEntry(entry) + } +} + func (logger *Logger) Trace(args ...interface{}) { logger.Log(TraceLevel, args...) } @@ -234,6 +247,45 @@ func (logger *Logger) Panic(args ...interface{}) { logger.Log(PanicLevel, args...) } +func (logger *Logger) TraceFn(fn LogFunction) { + logger.LogFn(TraceLevel, fn) +} + +func (logger *Logger) DebugFn(fn LogFunction) { + logger.LogFn(DebugLevel, fn) +} + +func (logger *Logger) InfoFn(fn LogFunction) { + logger.LogFn(InfoLevel, fn) +} + +func (logger *Logger) PrintFn(fn LogFunction) { + entry := logger.newEntry() + entry.Print(fn()...) + logger.releaseEntry(entry) +} + +func (logger *Logger) WarnFn(fn LogFunction) { + logger.LogFn(WarnLevel, fn) +} + +func (logger *Logger) WarningFn(fn LogFunction) { + logger.WarnFn(fn) +} + +func (logger *Logger) ErrorFn(fn LogFunction) { + logger.LogFn(ErrorLevel, fn) +} + +func (logger *Logger) FatalFn(fn LogFunction) { + logger.LogFn(FatalLevel, fn) + logger.Exit(1) +} + +func (logger *Logger) PanicFn(fn LogFunction) { + logger.LogFn(PanicLevel, fn) +} + func (logger *Logger) Logln(level Level, args ...interface{}) { if logger.IsLevelEnabled(level) { entry := logger.newEntry() From 14b02ee22a05135517377bd83c57a3f6a07cc13d Mon Sep 17 00:00:00 2001 From: Mark Phelps Date: Thu, 16 Apr 2020 11:13:51 -0400 Subject: [PATCH 536/547] Revert #1047 --- entry.go | 2 -- entry_test.go | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/entry.go b/entry.go index 27b14bfb1..f6e062a34 100644 --- a/entry.go +++ b/entry.go @@ -122,8 +122,6 @@ func (entry *Entry) WithField(key string, value interface{}) *Entry { // Add a map of fields to the Entry. func (entry *Entry) WithFields(fields Fields) *Entry { - entry.Logger.mu.Lock() - defer entry.Logger.mu.Unlock() data := make(Fields, len(entry.Data)+len(fields)) for k, v := range entry.Data { data[k] = v diff --git a/entry_test.go b/entry_test.go index 30eddaecc..b98e14ad6 100644 --- a/entry_test.go +++ b/entry_test.go @@ -210,7 +210,7 @@ func TestEntryWithIncorrectField(t *testing.T) { fn := func() {} - e := &Entry{Logger: New()} + e := Entry{} eWithFunc := e.WithFields(Fields{"func": fn}) eWithFuncPtr := e.WithFields(Fields{"funcPtr": &fn}) From eba264ff0bc6f7f63366b00fbf4c4b528a0af007 Mon Sep 17 00:00:00 2001 From: Thomas Lacroix Date: Thu, 23 Apr 2020 14:02:38 +0200 Subject: [PATCH 537/547] Adds flag to disable quotes in TextFormatter --- text_formatter.go | 6 ++++++ text_formatter_test.go | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/text_formatter.go b/text_formatter.go index 2d15a239f..431b5fd9a 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -37,6 +37,9 @@ type TextFormatter struct { // Force quoting of all values ForceQuote bool + // DisableQuote disables quoting for all values + DisableQuote bool + // Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/ EnvironmentOverrideColors bool @@ -292,6 +295,9 @@ func (f *TextFormatter) needsQuoting(text string) bool { if f.QuoteEmptyFields && len(text) == 0 { return true } + if f.DisableQuote { + return false + } for _, ch := range text { if !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || diff --git a/text_formatter_test.go b/text_formatter_test.go index 5d94ebb76..debf621cc 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -66,6 +66,14 @@ func TestQuoting(t *testing.T) { checkQuoting(false, errors.New("invalid")) checkQuoting(true, errors.New("invalid argument")) + // Test for quoting disabled + tf.DisableQuote = true + checkQuoting(false, "") + checkQuoting(false, "abcd") + checkQuoting(false, "foo\n\rbar") + checkQuoting(false, errors.New("invalid argument")) + tf.DisableQuote = false + // Test for quoting empty fields. tf.QuoteEmptyFields = true checkQuoting(true, "") From 5cb9357984942c2c99b58e5b724d53f2c2d033f4 Mon Sep 17 00:00:00 2001 From: Thomas Lacroix Date: Thu, 23 Apr 2020 17:16:50 +0200 Subject: [PATCH 538/547] Adds additional test cases for DisableQuote --- text_formatter_test.go | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/text_formatter_test.go b/text_formatter_test.go index debf621cc..5b1cc0ab5 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -59,6 +59,7 @@ func TestQuoting(t *testing.T) { checkQuoting(false, "foo@bar") checkQuoting(false, "foobar^") checkQuoting(false, "+/-_^@f.oobar") + checkQuoting(true, "foo\n\rbar") checkQuoting(true, "foobar$") checkQuoting(true, "&foobar") checkQuoting(true, "x y") @@ -66,25 +67,34 @@ func TestQuoting(t *testing.T) { checkQuoting(false, errors.New("invalid")) checkQuoting(true, errors.New("invalid argument")) - // Test for quoting disabled - tf.DisableQuote = true - checkQuoting(false, "") - checkQuoting(false, "abcd") - checkQuoting(false, "foo\n\rbar") - checkQuoting(false, errors.New("invalid argument")) - tf.DisableQuote = false - // Test for quoting empty fields. tf.QuoteEmptyFields = true checkQuoting(true, "") checkQuoting(false, "abcd") + checkQuoting(true, "foo\n\rbar") checkQuoting(true, errors.New("invalid argument")) // Test forcing quotes. tf.ForceQuote = true checkQuoting(true, "") checkQuoting(true, "abcd") + checkQuoting(true, "foo\n\rbar") + checkQuoting(true, errors.New("invalid argument")) + + // Test forcing quotes when also disabling them. + tf.DisableQuote = true + checkQuoting(true, "") + checkQuoting(true, "abcd") + checkQuoting(true, "foo\n\rbar") checkQuoting(true, errors.New("invalid argument")) + + // Test disabling quotes + tf.ForceQuote = false + tf.QuoteEmptyFields = false + checkQuoting(false, "") + checkQuoting(false, "abcd") + checkQuoting(false, "foo\n\rbar") + checkQuoting(false, errors.New("invalid argument")) } func TestEscaping(t *testing.T) { From 867f3eb18eae6c119f4030f0adda8a75a8dc311b Mon Sep 17 00:00:00 2001 From: ialidzhikov Date: Wed, 29 Apr 2020 00:25:29 +0300 Subject: [PATCH 539/547] Change CRLF line endings to LF Signed-off-by: ialidzhikov --- CHANGELOG.md | 2 +- appveyor.yml | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51a7ab0ca..270ed666d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ Fixes: # 1.4.0 This new release introduces: * Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848). - * Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter (#909, #911) + * Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter` (#909, #911) * Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919). Fixes: diff --git a/appveyor.yml b/appveyor.yml index 96c2ce15f..df9d65c3a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,14 +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 +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 dd3b7f5849012d3f6422a7417548036a7a943400 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Wed, 29 Apr 2020 08:56:01 +0200 Subject: [PATCH 540/547] update github.com/konsorten/go-windows-terminal-sequences dependency to v1.0.3 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 9ea6e841b..d41329679 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/sirupsen/logrus require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/konsorten/go-windows-terminal-sequences v1.0.1 + github.com/konsorten/go-windows-terminal-sequences v1.0.3 github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 golang.org/x/sys v0.0.0-20190422165155-953cdadca894 diff --git a/go.sum b/go.sum index 95a3f07de..49c690f23 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= From 27a0399f351bff4cb6b4f53a04dab479218aa908 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Wed, 29 Apr 2020 09:15:21 +0200 Subject: [PATCH 541/547] complete documetation on TextFormatter.DisableQuote --- text_formatter.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text_formatter.go b/text_formatter.go index 431b5fd9a..3c28b54ca 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -37,7 +37,9 @@ type TextFormatter struct { // Force quoting of all values ForceQuote bool - // DisableQuote disables quoting for all values + // DisableQuote disables quoting for all values. + // DisableQuote will have a lower priority than ForceQuote. + // If both of them are set to true, quote will be forced on all values. DisableQuote bool // Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/ From 893c724de25aaeed9e558ca6c00e04905f5638b1 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sat, 2 May 2020 15:06:20 +0200 Subject: [PATCH 542/547] update CHANGELOG.md with 1.5.0 and 1.6.0 version contents --- CHANGELOG.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 270ed666d..584026d67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,32 @@ +# 1.6.0 +Fixes: + * end of line cleanup + * revert the entry concurrency bug fix whic leads to deadlock under some circumstances + * update dependency on go-windows-terminal-sequences to fix a crash with go 1.14 + +Features: + * add an option to the `TextFormatter` to completely disable fields quoting + +# 1.5.0 +Code quality: + * add golangci linter run on travis + +Fixes: + * add mutex for hooks concurrent access on `Entry` data + * caller function field for go1.14 + * fix build issue for gopherjs target + +Feature: + * add an hooks/writer sub-package whose goal is to split output on different stream depending on the trace level + * add a `DisableHTMLEscape` option in the `JSONFormatter` + * add `ForceQuote` and `PadLevelText` options in the `TextFormatter` + # 1.4.2 * Fixes build break for plan9, nacl, solaris # 1.4.1 This new release introduces: * Enhance TextFormatter to not print caller information when they are empty (#944) - * Remove dependency on golang.org/x/crypto (#932, #943) + * Remove dependency on golang.org/x/crypto (#932, #943) Fixes: * Fix Entry.WithContext method to return a copy of the initial entry (#941) From fc5e1a116c3427f4a652c40880cf399cb2c090c3 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Sun, 12 Jan 2020 01:49:45 +0100 Subject: [PATCH 543/547] Simplify checkIfTerminal for Windows Instead of relying on EnableVirtualTerminalProcessing from github.com/konsorten/go-windows-terminal-sequences which just calls GetConsoleMode, sets ENABLE_VIRTUAL_TERMINAL_PROCESSING and calls SetConsoleMode with the new modified mode, implement it directly inside checkIfTerminal. This also avoids the duplicate call to GetConsoleMode. --- go.mod | 1 - go.sum | 4 ---- terminal_check_windows.go | 29 +++++++++++------------------ travis/install.sh | 2 +- 4 files changed, 12 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index d41329679..324cefd7b 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,6 @@ module github.com/sirupsen/logrus require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/konsorten/go-windows-terminal-sequences v1.0.3 github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 golang.org/x/sys v0.0.0-20190422165155-953cdadca894 diff --git a/go.sum b/go.sum index 49c690f23..c574edc5c 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,5 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= diff --git a/terminal_check_windows.go b/terminal_check_windows.go index 572889db2..2879eb50e 100644 --- a/terminal_check_windows.go +++ b/terminal_check_windows.go @@ -5,30 +5,23 @@ package logrus import ( "io" "os" - "syscall" - sequences "github.com/konsorten/go-windows-terminal-sequences" + "golang.org/x/sys/windows" ) -func initTerminal(w io.Writer) { - switch v := w.(type) { - case *os.File: - sequences.EnableVirtualTerminalProcessing(syscall.Handle(v.Fd()), true) - } -} - func checkIfTerminal(w io.Writer) bool { - var ret bool switch v := w.(type) { case *os.File: + handle := windows.Handle(v.Fd()) var mode uint32 - err := syscall.GetConsoleMode(syscall.Handle(v.Fd()), &mode) - ret = (err == nil) - default: - ret = false - } - if ret { - initTerminal(w) + if err := windows.GetConsoleMode(handle, &mode); err != nil { + return false + } + mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING + if err := windows.SetConsoleMode(handle, mode); err != nil { + return false + } + return true } - return ret + return false } diff --git a/travis/install.sh b/travis/install.sh index 6fa948646..5fc40dd81 100755 --- a/travis/install.sh +++ b/travis/install.sh @@ -18,5 +18,5 @@ fi if [[ "$GO111MODULE" == "off" ]]; then # Should contain all regular (not indirect) modules from go.mod - go get github.com/stretchr/testify golang.org/x/sys/unix github.com/konsorten/go-windows-terminal-sequences + go get github.com/stretchr/testify golang.org/x/sys/unix golang.org/x/sys/windows fi From 5ad417de85af78df5ece383c268b74fee29590b4 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Tue, 19 May 2020 17:02:33 +0200 Subject: [PATCH 544/547] Improve tests for logger.*Fn functions --- example_function_test.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/example_function_test.go b/example_function_test.go index f61460eca..dda890d83 100644 --- a/example_function_test.go +++ b/example_function_test.go @@ -1,26 +1,31 @@ package logrus_test import ( - "fmt" - log "github.com/sirupsen/logrus" "testing" + + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" ) func TestLogger_LogFn(t *testing.T) { log.SetFormatter(&log.JSONFormatter{}) log.SetLevel(log.WarnLevel) + notCalled := 0 log.InfoFn(func() []interface{} { - fmt.Println("This is never run") - return []interface{} { + notCalled++ + return []interface{}{ "Hello", } }) + assert.Equal(t, 0, notCalled) + called := 0 log.ErrorFn(func() []interface{} { - fmt.Println("This runs") - return []interface{} { + called++ + return []interface{}{ "Oopsi", } }) + assert.Equal(t, 1, called) } From 393fa3bae3cb886552eed41ab0ca9852ec6c2c92 Mon Sep 17 00:00:00 2001 From: Sohel Date: Tue, 26 May 2020 10:26:28 +0530 Subject: [PATCH 545/547] Update doc for new logger Update default formatter for new logger from JsonFormatter to TextFormatter --- logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logger.go b/logger.go index dae5e5708..dbf627c97 100644 --- a/logger.go +++ b/logger.go @@ -75,7 +75,7 @@ func (mw *MutexWrap) Disable() { // // var log = &logrus.Logger{ // Out: os.Stderr, -// Formatter: new(logrus.JSONFormatter), +// Formatter: new(logrus.TextFormatter), // Hooks: make(logrus.LevelHooks), // Level: logrus.DebugLevel, // } From d2fc9bde39c0b475ad1c06c3013242fc70c2298e Mon Sep 17 00:00:00 2001 From: David Bariod Date: Thu, 28 May 2020 10:47:50 +0200 Subject: [PATCH 546/547] Add an API to plug a custom buffer free item mangement system --- buffer_pool.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ entry.go | 14 +++++--------- 2 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 buffer_pool.go diff --git a/buffer_pool.go b/buffer_pool.go new file mode 100644 index 000000000..4545dec07 --- /dev/null +++ b/buffer_pool.go @@ -0,0 +1,52 @@ +package logrus + +import ( + "bytes" + "sync" +) + +var ( + bufferPool BufferPool +) + +type BufferPool interface { + Put(*bytes.Buffer) + Get() *bytes.Buffer +} + +type defaultPool struct { + pool *sync.Pool +} + +func (p *defaultPool) Put(buf *bytes.Buffer) { + p.pool.Put(buf) +} + +func (p *defaultPool) Get() *bytes.Buffer { + return p.pool.Get().(*bytes.Buffer) +} + +func getBuffer() *bytes.Buffer { + return bufferPool.Get() +} + +func putBuffer(buf *bytes.Buffer) { + buf.Reset() + bufferPool.Put(buf) +} + +// SetBufferPool allows to replace the default logrus buffer pool +// to better meets the specific needs of an application. +func SetBufferPool(bp BufferPool) { + bufferPool = bp +} + +func init() { + SetBufferPool(&defaultPool{ + pool: &sync.Pool{ + New: func() interface{} { + return new(bytes.Buffer) + }, + }, + }) +} diff --git a/entry.go b/entry.go index f6e062a34..5a5cbfe7c 100644 --- a/entry.go +++ b/entry.go @@ -13,7 +13,6 @@ import ( ) var ( - bufferPool *sync.Pool // qualified package name, cached at first use logrusPackage string @@ -31,12 +30,6 @@ const ( ) func init() { - bufferPool = &sync.Pool{ - New: func() interface{} { - return new(bytes.Buffer) - }, - } - // start at the bottom of the stack before the package-name cache is primed minimumCallerDepth = 1 } @@ -243,9 +236,12 @@ func (entry Entry) log(level Level, msg string) { entry.fireHooks() - buffer = bufferPool.Get().(*bytes.Buffer) + buffer = getBuffer() + defer func() { + entry.Buffer = nil + putBuffer(buffer) + }() buffer.Reset() - defer bufferPool.Put(buffer) entry.Buffer = buffer entry.write() From 495016bb0745f128edf3f4af265c5eeeff3afa51 Mon Sep 17 00:00:00 2001 From: cgxxv Date: Wed, 16 Sep 2020 12:27:46 +0800 Subject: [PATCH 547/547] support caller manually --- entry.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/entry.go b/entry.go index 5a5cbfe7c..71b7120c6 100644 --- a/entry.go +++ b/entry.go @@ -94,6 +94,19 @@ func (entry *Entry) String() (string, error) { return str, nil } +// Add a caller field to the Entry. +func (entry *Entry) WithCaller() *Entry { + frame := getCaller() + if frame != nil { + return entry.WithFields(Fields{ + FieldKeyFile: fmt.Sprintf("%s:%d", frame.File, frame.Line), + FieldKeyFunc: frame.Function, + }) + } + + return entry +} + // Add an error as single field (using the key defined in ErrorKey) to the Entry. func (entry *Entry) WithError(err error) *Entry { return entry.WithField(ErrorKey, err)