Skip to content

Commit

Permalink
Add limits.
Browse files Browse the repository at this point in the history
  • Loading branch information
cstorey-monzo committed Mar 12, 2024
1 parent 29ed051 commit acf7d7a
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 2 deletions.
21 changes: 19 additions & 2 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,20 +162,37 @@ func (p *Error) StackTrace() []uintptr {
// encounter more than one terror in the chain with a stack frame, we'll print
// each one, separated by three hyphens on their own line.
func (p *Error) StackString() string {
// 32,000 seems like a reasonable limit for a stack trace. Otherwise, we risk
// overwhelming downstream systems.
return StackStringWithMaxSize(p, 32000)
}

func StackStringWithMaxSize(p *Error, sizeLimit int) string {
// if we run into this many causes, we've likely run into something absurd. Like
// a self causing error.
const maxCausalDepth = 1024
var buffer strings.Builder
terr := p
var causalDepth int
outer:
for terr != nil {
if buffer.Len() != 0 && len(terr.StackFrames) > 0 {
fmt.Fprintf(&buffer, "\n---")
}
for _, frame := range terr.StackFrames {
// 10 seems like a reasonable estimate of how large the rest of the line would be.
estimatedLineLen := len(frame.Filename) + len(frame.Method) + 16
if estimatedLineLen+buffer.Len() > sizeLimit {
break outer
}
fmt.Fprintf(&buffer, "\n %s:%d in %s", frame.Filename, frame.Line, frame.Method)
}

if tcause, ok := terr.cause.(*Error); ok {
if tcause, ok := terr.cause.(*Error); ok && causalDepth < maxCausalDepth {
terr = tcause
causalDepth += 1
} else {
break
break outer
}
}

Expand Down
21 changes: 21 additions & 0 deletions errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -595,3 +595,24 @@ func TestStackStringChasesCausalChain(t *testing.T) {
t.Log(ss)
assert.Contains(t, ss, "failyFunction")
}

func TestCircularErrorProducesFiniteOutputWithStackFrames(t *testing.T) {
orig := failyFunction()
err := Augment(orig, "something may be up", nil)
terr := err.(*Error)
terr.cause = terr
terr.StackFrames = orig.(*Error).StackFrames
ss := terr.StackString()

// The default field size limit used in elastic-slog. It's kind of arbitrary, but it'll do for now.
assert.Less(t, len(ss), 32000)
assert.GreaterOrEqual(t, len(ss), 32000-1000)
}
func TestCircularErrorProducesFiniteOutputWithoutStackFrames(t *testing.T) {
err := Augment(failyFunction(), "something may be up", nil)
terr := err.(*Error)
terr.cause = terr
ss := terr.StackString()
// There's no actual stack in the causal cycle, so we don't render anything here.
assert.Empty(t, ss)
}

0 comments on commit acf7d7a

Please sign in to comment.