From ff92509e2c44fc306682f4a4160e4a189ff69fee Mon Sep 17 00:00:00 2001 From: David Bariod Date: Fri, 12 Oct 2018 06:53:57 +0200 Subject: [PATCH 01/52] 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 0c525822dcfe68ae420b32def223c5c7783f60c6 Mon Sep 17 00:00:00 2001 From: Fabien Meurillon Date: Wed, 24 Oct 2018 11:03:07 +0200 Subject: [PATCH 02/52] 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 2cafb78db2ffe7e4592a07991f77f9105db898ab 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 03/52] 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 c7183bf62988354bac21887606dbb540dc4be8f7 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 04/52] 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 eab2c444ac0515d86a8eb907e85f5a356b1e74c0 Mon Sep 17 00:00:00 2001 From: xrstf Date: Fri, 9 Nov 2018 13:34:45 +0100 Subject: [PATCH 05/52] 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 e9026580bf4499404ce258b90d8ea3833919b46e Mon Sep 17 00:00:00 2001 From: ceriath Date: Fri, 16 Nov 2018 15:17:01 +0100 Subject: [PATCH 06/52] 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 f1b98e4006fd58afa9cc5b28cd665b96519ef166 Mon Sep 17 00:00:00 2001 From: ceriath Date: Fri, 16 Nov 2018 15:19:37 +0100 Subject: [PATCH 07/52] 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 0c5e33c7e05555857150a7651ba23f8193409f06 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 08/52] 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 08e8d6501d300248a75c6375cfd3444b2469bb71 Mon Sep 17 00:00:00 2001 From: Maxim Sukharev Date: Wed, 5 Dec 2018 19:14:19 +0100 Subject: [PATCH 09/52] 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 d96201375623f7f4dffa8f39457c6f00afaf922e Mon Sep 17 00:00:00 2001 From: Ceriath Date: Sun, 9 Dec 2018 21:47:44 +0100 Subject: [PATCH 10/52] 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 9abefb94aab86a2831fd8a0da4f5d2ebd58272cd Mon Sep 17 00:00:00 2001 From: David Bariod Date: Fri, 14 Dec 2018 17:01:34 +0100 Subject: [PATCH 11/52] 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 a6668e7a605443af72ef75804a9f9f24a99f79ae Mon Sep 17 00:00:00 2001 From: Lisa Ugray Date: Thu, 15 Nov 2018 14:34:22 -0500 Subject: [PATCH 12/52] 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 ff695daa3663a818e6a54032d49328e41acdcce5 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Wed, 26 Dec 2018 17:43:14 +0100 Subject: [PATCH 13/52] 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 e8fd0ba60913563af62ac5011b8bc436da0931b9 Mon Sep 17 00:00:00 2001 From: Lisa Ugray Date: Wed, 2 Jan 2019 14:58:51 -0500 Subject: [PATCH 14/52] 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 bd9534b7993e97ff512897bfb3f35f749247a1d6 Mon Sep 17 00:00:00 2001 From: Lisa Ugray Date: Wed, 2 Jan 2019 14:59:36 -0500 Subject: [PATCH 15/52] 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 eef6b768ab01a0598a0a6db97bad2a37d31df1d1 Mon Sep 17 00:00:00 2001 From: Rich Poirier Date: Fri, 4 Jan 2019 17:46:35 -0800 Subject: [PATCH 16/52] 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 78fb3852d92683dc28da6cc3d5f965100677c27d Mon Sep 17 00:00:00 2001 From: Sergey Romanov Date: Sat, 12 Jan 2019 00:22:21 +0500 Subject: [PATCH 17/52] 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 a99ca4776daa02a26c3f57289df035d89fe7113f Mon Sep 17 00:00:00 2001 From: David Bariod Date: Thu, 17 Jan 2019 09:37:03 +0100 Subject: [PATCH 18/52] 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 0f544bf2781d9e7282479d887b263313f7c8840d Mon Sep 17 00:00:00 2001 From: David Bariod Date: Thu, 17 Jan 2019 13:42:01 +0100 Subject: [PATCH 19/52] 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 f61e48bb8e7cd84be2602880c97e0c5952827973 Mon Sep 17 00:00:00 2001 From: Jiang Xin Date: Tue, 22 Jan 2019 20:08:19 +0800 Subject: [PATCH 20/52] 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 68a2b575f1cb2939d572e0a5fdc17c25b7350794 Mon Sep 17 00:00:00 2001 From: Anton Fisher Date: Sat, 26 Jan 2019 14:16:28 -0800 Subject: [PATCH 21/52] 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 4ea4861398d99a2d05be29675c5b74caf7bea95e Mon Sep 17 00:00:00 2001 From: Georgi Dimitrov Date: Tue, 29 Jan 2019 18:29:38 +0000 Subject: [PATCH 22/52] 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 774bb8e43fb8940119551b113ed27281d501b824 Mon Sep 17 00:00:00 2001 From: CodeLingo Bot Date: Tue, 5 Feb 2019 05:52:27 +0000 Subject: [PATCH 23/52] 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 c4e4882020c0da433ed03f82be0bed3a497dbacc Mon Sep 17 00:00:00 2001 From: Gavin Cabbage Date: Wed, 6 Feb 2019 14:51:07 -0500 Subject: [PATCH 24/52] 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 4f5fd631f16452fbd023813c1eb7dbd67130cb0c Mon Sep 17 00:00:00 2001 From: noushavandijk Date: Wed, 20 Feb 2019 18:22:53 +0100 Subject: [PATCH 25/52] 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 5e9b246bea7043925e54c10910d01f2de3be9015 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Wed, 27 Feb 2019 13:02:20 +0100 Subject: [PATCH 26/52] 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 ffec2f2e0a4f12af99b41bbd0526fca75056ff04 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Wed, 27 Feb 2019 13:34:02 +0100 Subject: [PATCH 27/52] 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 5c2b39a4f8d3edd100748d226a77acb4649d79ec Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sun, 3 Mar 2019 11:52:04 +0100 Subject: [PATCH 28/52] 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 99a5172d62d45e607945ff99722c24a7db95e7a0 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sun, 3 Mar 2019 11:53:51 +0100 Subject: [PATCH 29/52] 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 b9d451406decb5cab037cc3a473547e0eb940370 Mon Sep 17 00:00:00 2001 From: georlav Date: Mon, 4 Mar 2019 20:38:10 +0200 Subject: [PATCH 30/52] 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 cf1b9fd15e80ce9ce658546d333f031de2d08331 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Wed, 6 Mar 2019 14:08:02 +0100 Subject: [PATCH 31/52] 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 68e41f673a1c0f32433d937ff6665496002f7a85 Mon Sep 17 00:00:00 2001 From: Adam Renberg Tamm Date: Mon, 11 Mar 2019 11:47:03 +0100 Subject: [PATCH 32/52] 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 02141df9f056e94110db113dbaabe23e8d2adf1f Mon Sep 17 00:00:00 2001 From: Adam Renberg Tamm Date: Mon, 11 Mar 2019 16:26:46 +0100 Subject: [PATCH 33/52] 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 c0765944301f7eb1a3476c419ed415154625b370 Mon Sep 17 00:00:00 2001 From: Emil Hessman Date: Sun, 24 Mar 2019 16:01:10 +0100 Subject: [PATCH 34/52] 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 10ff0d07c31daf527d014cdf950c69b49b081a51 Mon Sep 17 00:00:00 2001 From: Andrey Tcherepanov Date: Tue, 26 Mar 2019 10:19:59 -0600 Subject: [PATCH 35/52] 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 7de3dd8c8bcd66620fd36cc2f77c8f3d1508c612 Mon Sep 17 00:00:00 2001 From: Andrey Tcherepanov Date: Tue, 26 Mar 2019 11:01:50 -0600 Subject: [PATCH 36/52] 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 41ee4dd36547ea07a3ffc951db6c341988569d5d Mon Sep 17 00:00:00 2001 From: Andrey Tcherepanov Date: Tue, 26 Mar 2019 14:53:49 -0600 Subject: [PATCH 37/52] 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 5d8c3bffc9ce192a8c4f61e46f3f174969149cc6 Mon Sep 17 00:00:00 2001 From: Andrey Tcherepanov Date: Wed, 27 Mar 2019 10:59:38 -0600 Subject: [PATCH 38/52] 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 c49ef1d4bf8ba0994a478d210d58446327524490 Mon Sep 17 00:00:00 2001 From: Jessica Paczuski Date: Thu, 28 Mar 2019 11:32:49 +0100 Subject: [PATCH 39/52] 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 7d700cdff022e336606a06a82f6289ef694a13ab Mon Sep 17 00:00:00 2001 From: Andrey Tcherepanov Date: Thu, 28 Mar 2019 13:14:38 -0600 Subject: [PATCH 40/52] 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 38bc297a3db027003d165efe3483872065b4e00c Mon Sep 17 00:00:00 2001 From: Haoran Xu Date: Fri, 29 Mar 2019 14:04:26 +0800 Subject: [PATCH 41/52] 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 3e06420226535b37c2ad9f55e41815c518a3f687 Mon Sep 17 00:00:00 2001 From: Andrey Tcherepanov Date: Mon, 1 Apr 2019 10:15:09 -0600 Subject: [PATCH 42/52] 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 ede5b639cd53235df193ab3c501d122be5195b17 Mon Sep 17 00:00:00 2001 From: Andrey Tcherepanov Date: Mon, 1 Apr 2019 10:16:11 -0600 Subject: [PATCH 43/52] 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 6c615e1abed4589fe553415ab09b419938f65152 Mon Sep 17 00:00:00 2001 From: Haoran Xu Date: Tue, 2 Apr 2019 11:43:56 +0800 Subject: [PATCH 44/52] 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 8bdbc7bcc01dcbb8ec23dc8a28e332258d25251f Mon Sep 17 00:00:00 2001 From: David Bariod Date: Tue, 2 Apr 2019 18:14:07 +0200 Subject: [PATCH 45/52] 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 c1b61542d78094ef1a6fee0746528a251730141f Mon Sep 17 00:00:00 2001 From: David Bariod Date: Wed, 3 Apr 2019 10:46:44 +0200 Subject: [PATCH 46/52] 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 5521996833c095f5ac75d99687cef8ad344ded12 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 47/52] 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 1a601d20598f07b6397f1c2b3c7d659e3d1b4894 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Fri, 10 May 2019 06:44:02 +0200 Subject: [PATCH 48/52] 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 1bc909a4f82b24e40552305da4a6907cabc48e66 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 49/52] 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 f2849a8fb20867468d5da0ea8137484d49c12bd7 Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sat, 18 May 2019 10:27:12 +0200 Subject: [PATCH 50/52] 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 744fc4caad20588e394ddd7a1af4d7a9ecabe3ac Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sat, 18 May 2019 11:44:18 +0200 Subject: [PATCH 51/52] 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 839c75faf7f98a33d445d181f3018b5c3409a45e Mon Sep 17 00:00:00 2001 From: David Bariod Date: Sat, 18 May 2019 12:38:48 +0200 Subject: [PATCH 52/52] 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)