Browse Source

Capture and print stacktrace when Error or Errorf is called

Signed-off-by: Robert Lin <robertlin1@gmail.com>
Robert Lin 6 years ago
parent
commit
6018733826
2 changed files with 35 additions and 6 deletions
  1. 26 2
      internal/core/logger.go
  2. 9 4
      internal/core/logger_test.go

+ 26 - 2
internal/core/logger.go

@@ -3,6 +3,8 @@ package core
 import (
 import (
 	"log"
 	"log"
 	"os"
 	"os"
+	"runtime/debug"
+	"strings"
 )
 )
 
 
 // ConfigLogger is the key for the pipeline's logger
 // ConfigLogger is the key for the pipeline's logger
@@ -48,7 +50,29 @@ func (d *DefaultLogger) Warn(v ...interface{}) { d.W.Print(v...) }
 func (d *DefaultLogger) Warnf(f string, v ...interface{}) { d.W.Printf(f, v...) }
 func (d *DefaultLogger) Warnf(f string, v ...interface{}) { d.W.Printf(f, v...) }
 
 
 // Error writes to the error logger
 // Error writes to the error logger
-func (d *DefaultLogger) Error(v ...interface{}) { d.E.Print(v...) }
+func (d *DefaultLogger) Error(v ...interface{}) {
+	d.E.Print(v...)
+	d.logStacktraceToErr()
+}
 
 
 // Errorf writes to the error logger
 // Errorf writes to the error logger
-func (d *DefaultLogger) Errorf(f string, v ...interface{}) { d.E.Printf(f, v...) }
+func (d *DefaultLogger) Errorf(f string, v ...interface{}) {
+	d.E.Printf(f, v...)
+	d.logStacktraceToErr()
+}
+
+// logStacktraceToErr prints a stacktrace to the logger's error output.
+// It skips 4 levels that aren't meaningful to a logged stacktrace:
+// * debug.Stack()
+// * core.captureStacktrace()
+// * DefaultLogger::logStacktraceToErr()
+// * DefaultLogger::Error() or DefaultLogger::Errorf()
+func (d *DefaultLogger) logStacktraceToErr() {
+	d.E.Println("stacktrace:\n" + strings.Join(captureStacktrace(0), "\n"))
+}
+
+func captureStacktrace(skip int) []string {
+	stack := string(debug.Stack())
+	lines := strings.Split(stack, "\n")
+	return lines[2*skip+1:]
+}

+ 9 - 4
internal/core/logger_test.go

@@ -34,19 +34,24 @@ func TestLogger(t *testing.T) {
 
 
 	l.Warn(v...)
 	l.Warn(v...)
 	assert.Contains(t, wBuf.String(), "[WARN]")
 	assert.Contains(t, wBuf.String(), "[WARN]")
-	iBuf.Reset()
+	wBuf.Reset()
 
 
 	l.Warnf(f, v...)
 	l.Warnf(f, v...)
 	assert.Contains(t, wBuf.String(), "[WARN]")
 	assert.Contains(t, wBuf.String(), "[WARN]")
 	assert.Contains(t, wBuf.String(), "-")
 	assert.Contains(t, wBuf.String(), "-")
-	iBuf.Reset()
+	wBuf.Reset()
 
 
 	l.Error(v...)
 	l.Error(v...)
 	assert.Contains(t, eBuf.String(), "[ERROR]")
 	assert.Contains(t, eBuf.String(), "[ERROR]")
-	iBuf.Reset()
+	assert.Contains(t, eBuf.String(), "internal/core.TestLogger")
+	assert.Contains(t, eBuf.String(), "internal/core/logger_test.go:44")
+	eBuf.Reset()
 
 
 	l.Errorf(f, v...)
 	l.Errorf(f, v...)
 	assert.Contains(t, eBuf.String(), "[ERROR]")
 	assert.Contains(t, eBuf.String(), "[ERROR]")
 	assert.Contains(t, eBuf.String(), "-")
 	assert.Contains(t, eBuf.String(), "-")
-	iBuf.Reset()
+	assert.Contains(t, eBuf.String(), "internal/core.TestLogger")
+	assert.Contains(t, eBuf.String(), "internal/core/logger_test.go:50")
+	println(eBuf.String())
+	eBuf.Reset()
 }
 }