Skip to content

Commit a47b99c

Browse files
authored
Make stacktrace logging handle nested errors (#4573)
We now iterate through the error layers until we find the stacktrace - Previously any level of nesting prevented the stacktrace being shown Also added stacktrace logging to idealised share error logging Signed-off-by: JamesMurkin <jamesmurkin@hotmail.com>
1 parent b79b36f commit a47b99c

File tree

3 files changed

+102
-18
lines changed

3 files changed

+102
-18
lines changed

internal/common/logging/logger.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,39 @@ func (l *Logger) WithError(err error) *Logger {
9393
// WithStacktrace returns a new Logger with the error and (if available) the stacktrace added as fields
9494
func (l *Logger) WithStacktrace(err error) *Logger {
9595
logger := l.WithError(err)
96-
if stackErr, ok := err.(stackTracer); ok {
97-
return logger.WithField("stacktrace", fmt.Sprintf("%v", stackErr.StackTrace()))
96+
97+
stackTrace := getStackTrace(err)
98+
if stackTrace != nil && len(stackTrace) > 0 {
99+
return logger.WithField("stacktrace", fmt.Sprintf("%v", stackTrace))
98100
}
99101
return logger
100102
}
101103

104+
// Will look through the Cause chain until it finds a stacktrace
105+
// Will not nest deeper than 10 layers, will return nil if the stacktrace isn't reached after that many iterations
106+
func getStackTrace(err error) errors.StackTrace {
107+
var stackErr error
108+
count := 0
109+
for {
110+
count++
111+
if count > 10 {
112+
break
113+
}
114+
if _, ok := err.(stackTracer); ok {
115+
stackErr = err
116+
}
117+
if causerErr, ok := err.(causer); ok {
118+
err = causerErr.Cause()
119+
} else {
120+
break
121+
}
122+
}
123+
if stackErr != nil {
124+
return stackErr.(stackTracer).StackTrace()
125+
}
126+
return nil
127+
}
128+
102129
// WithField returns a new Logger with the key-value pair added as a new field
103130
func (l *Logger) WithField(key string, value any) *Logger {
104131
return &Logger{
@@ -134,3 +161,7 @@ func (l *Logger) WithCallerSkip(skip int) *Logger {
134161
type stackTracer interface {
135162
StackTrace() errors.StackTrace
136163
}
164+
165+
type causer interface {
166+
Cause() error
167+
}

internal/common/logging/logger_test.go

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,22 +77,75 @@ func TestWithError(t *testing.T) {
7777
}
7878

7979
func TestWithStacktrace(t *testing.T) {
80-
logger, buf := testLogger()
81-
err := errors.New("test error")
82-
logger = logger.WithStacktrace(err)
83-
84-
logger.Info("test message")
85-
86-
assertLogLineExpected(
87-
t,
88-
&testLogEntry{
89-
Level: "info",
90-
Message: "test message",
91-
Error: "test error",
92-
Stacktrace: fmt.Sprintf("%v", err.(stackTracer).StackTrace()),
80+
errorsNew := errors.New("test error")
81+
errorsErrorf := errors.Errorf("test error")
82+
errorsWithStack := errors.WithStack(fmt.Errorf("inner error"))
83+
innerErrorWithStackTrace := errors.Errorf("inner error")
84+
errorsWithMessageNested := errors.WithMessage(innerErrorWithStackTrace, "outer message")
85+
errorsWithMessageNested2 := errors.WithMessage(errorsWithMessageNested, "even outer message")
86+
tests := map[string]struct {
87+
err error
88+
expectedLog *testLogEntry
89+
}{
90+
"errors.New": {
91+
err: errorsNew,
92+
expectedLog: &testLogEntry{
93+
Level: "info",
94+
Message: "test message",
95+
Error: "test error",
96+
Stacktrace: fmt.Sprintf("%v", errorsNew.(stackTracer).StackTrace()),
97+
},
9398
},
94-
buf,
95-
)
99+
"errors.ErrorF": {
100+
err: errorsErrorf,
101+
expectedLog: &testLogEntry{
102+
Level: "info",
103+
Message: "test message",
104+
Error: "test error",
105+
Stacktrace: fmt.Sprintf("%v", errorsErrorf.(stackTracer).StackTrace()),
106+
},
107+
},
108+
"errors.WithStack": {
109+
err: errorsWithStack,
110+
expectedLog: &testLogEntry{
111+
Level: "info",
112+
Message: "test message",
113+
Error: "inner error",
114+
Stacktrace: fmt.Sprintf("%v", errorsWithStack.(stackTracer).StackTrace()),
115+
},
116+
},
117+
"errors.WithMessage - nested": {
118+
err: errorsWithMessageNested,
119+
expectedLog: &testLogEntry{
120+
Level: "info",
121+
Message: "test message",
122+
Error: "outer message: inner error",
123+
Stacktrace: fmt.Sprintf("%v", innerErrorWithStackTrace.(stackTracer).StackTrace()),
124+
},
125+
},
126+
"errors.WithMessage - nested 2": {
127+
err: errorsWithMessageNested2,
128+
expectedLog: &testLogEntry{
129+
Level: "info",
130+
Message: "test message",
131+
Error: "even outer message: outer message: inner error",
132+
Stacktrace: fmt.Sprintf("%v", innerErrorWithStackTrace.(stackTracer).StackTrace()),
133+
},
134+
},
135+
}
136+
for name, tc := range tests {
137+
t.Run(name, func(t *testing.T) {
138+
logger, buf := testLogger()
139+
logger = logger.WithStacktrace(tc.err)
140+
logger.Info("test message")
141+
142+
assertLogLineExpected(
143+
t,
144+
tc.expectedLog,
145+
buf,
146+
)
147+
})
148+
}
96149
}
97150

98151
func TestLogAtLevel(t *testing.T) {

internal/scheduler/scheduling/scheduling_algo.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ func (l *FairSchedulingAlgo) SchedulePool(
577577
resourceUnit,
578578
)
579579
if idealisedShareErr != nil {
580-
log.Warnf("failed to calculated idealised share for pool %s - %s", fsctx.pool, idealisedShareErr)
580+
log.WithStacktrace(idealisedShareErr).Warnf("failed to calculated idealised share for pool %s - %s", fsctx.pool, idealisedShareErr)
581581
}
582582

583583
scheduler := NewPreemptingQueueScheduler(

0 commit comments

Comments
 (0)