Repository: sirupsen/logrus
Pull request #837 (2018-10-06 10:11:23)

Implements #813 - Add option to panic in `test.NewNullLogger`
Add a field in `Logger` for `Exit` implementation which defaults to `Exit()`.
This can be used in `NullLogger()` to replace calls to `os.Exit()` with `panic()`


Patch:
diff --git a/entry.go b/entry.go
index ca634a6..225bdb6 100644
--- a/entry.go
+++ b/entry.go
@@ -198,7 +198,7 @@ func (entry *Entry) Fatal(args ...interface{}) {
 	if entry.Logger.IsLevelEnabled(FatalLevel) {
 		entry.log(FatalLevel, fmt.Sprint(args...))
 	}
-	Exit(1)
+	entry.Logger.Exit(1)
 }
 
 func (entry *Entry) Panic(args ...interface{}) {
@@ -246,7 +246,7 @@ func (entry *Entry) Fatalf(format string, args ...interface{}) {
 	if entry.Logger.IsLevelEnabled(FatalLevel) {
 		entry.Fatal(fmt.Sprintf(format, args...))
 	}
-	Exit(1)
+	entry.Logger.Exit(1)
 }
 
 func (entry *Entry) Panicf(format string, args ...interface{}) {
@@ -293,7 +293,7 @@ func (entry *Entry) Fatalln(args ...interface{}) {
 	if entry.Logger.IsLevelEnabled(FatalLevel) {
 		entry.Fatal(entry.sprintlnn(args...))
 	}
-	Exit(1)
+	entry.Logger.Exit(1)
 }
 
 func (entry *Entry) Panicln(args ...interface{}) {
diff --git a/logger.go b/logger.go
index b67bfcb..1c934ed 100644
--- a/logger.go
+++ b/logger.go
@@ -32,8 +32,12 @@ type Logger struct {
 	mu MutexWrap
 	// Reusable empty entry
 	entryPool sync.Pool
+	// Function to exit the application, defaults to `os.Exit()`
+	ExitFunc exitFunc
 }
 
+type exitFunc func(int)
+
 type MutexWrap struct {
 	lock     sync.Mutex
 	disabled bool
@@ -73,6 +77,7 @@ func New() *Logger {
 		Formatter: new(TextFormatter),
 		Hooks:     make(LevelHooks),
 		Level:     InfoLevel,
+		ExitFunc:  os.Exit,
 	}
 }
 
@@ -173,7 +178,7 @@ func (logger *Logger) Fatalf(format string, args ...interface{}) {
 		entry.Fatalf(format, args...)
 		logger.releaseEntry(entry)
 	}
-	Exit(1)
+	logger.Exit(1)
 }
 
 func (logger *Logger) Panicf(format string, args ...interface{}) {
@@ -236,7 +241,7 @@ func (logger *Logger) Fatal(args ...interface{}) {
 		entry.Fatal(args...)
 		logger.releaseEntry(entry)
 	}
-	Exit(1)
+	logger.Exit(1)
 }
 
 func (logger *Logger) Panic(args ...interface{}) {
@@ -299,7 +304,7 @@ func (logger *Logger) Fatalln(args ...interface{}) {
 		entry.Fatalln(args...)
 		logger.releaseEntry(entry)
 	}
-	Exit(1)
+	logger.Exit(1)
 }
 
 func (logger *Logger) Panicln(args ...interface{}) {
@@ -310,6 +315,14 @@ func (logger *Logger) Panicln(args ...interface{}) {
 	}
 }
 
+func (logger *Logger) Exit(code int) {
+	runHandlers()
+	if logger.ExitFunc == nil {
+		logger.ExitFunc = os.Exit
+	}
+	logger.ExitFunc(code)
+}
+
 //When file is opened with appending mode, it's safe to
 //write concurrently to a file (within 4k message on Linux).
 //In these cases user can choose to disable the lock.
diff --git a/logrus.go b/logrus.go
index fa0b9de..6fff506 100644
--- a/logrus.go
+++ b/logrus.go
@@ -69,7 +69,7 @@ const (
 	// PanicLevel level, highest level of severity. Logs and then calls panic with the
 	// message passed to Debug, Info, ...
 	PanicLevel Level = iota
-	// FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the
+	// FatalLevel level. Logs and then calls `logger.Exit(1)`. It will exit even if the
 	// logging level is set to Panic.
 	FatalLevel
 	// ErrorLevel level. Logs. Used for errors that should definitely be noted.


Test patch:
diff --git a/hooks/test/test_test.go b/hooks/test/test_test.go
index d6f6d30..636bad5 100644
--- a/hooks/test/test_test.go
+++ b/hooks/test/test_test.go
@@ -71,3 +71,15 @@ func TestLoggingWithHooksRace(t *testing.T) {
 	entries := hook.AllEntries()
 	assert.Equal(100, len(entries))
 }
+
+func TestFatalWithAlternateExit(t *testing.T) {
+	assert := assert.New(t)
+
+	logger, hook := NewNullLogger()
+	logger.ExitFunc = func(code int) {}
+
+	logger.Fatal("something went very wrong")
+	assert.Equal(logrus.FatalLevel, hook.LastEntry().Level)
+	assert.Equal("something went very wrong", hook.LastEntry().Message)
+	assert.Equal(1, len(hook.Entries))
+}