Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove repeated error stack file lines among stacks for package gerror #2199

Merged
merged 2 commits into from
Oct 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 0 additions & 49 deletions errors/gerror/gerror_error_format.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,8 @@
package gerror

import (
"bytes"
"fmt"
"io"
"runtime"
"strings"

"github.com/gogf/gf/v2/internal/consts"
)

// Format formats the frame according to the fmt.Formatter interface.
Expand Down Expand Up @@ -43,47 +38,3 @@ func (err *Error) Format(s fmt.State, verb rune) {
}
}
}

// formatSubStack formats the stack for error.
func formatSubStack(st stack, buffer *bytes.Buffer) {
if st == nil {
return
}
index := 1
space := " "
for _, p := range st {
if fn := runtime.FuncForPC(p - 1); fn != nil {
file, line := fn.FileLine(p - 1)
if isUsingBriefStack {
// filter whole GoFrame packages stack paths.
if strings.Contains(file, consts.StackFilterKeyForGoFrame) {
continue
}
} else {
// package path stack filtering.
if strings.Contains(file, stackFilterKeyLocal) {
continue
}
}
// Avoid stack string like "`autogenerated`"
if strings.Contains(file, "<") {
continue
}
// Ignore GO ROOT paths.
if goRootForFilter != "" &&
len(file) >= len(goRootForFilter) &&
file[0:len(goRootForFilter)] == goRootForFilter {
continue
}
// Graceful indent.
if index > 9 {
space = " "
}
buffer.WriteString(fmt.Sprintf(
" %d).%s%s\n \t%s:%d\n",
index, space, fn.Name(), file, line,
))
index++
}
}
}
145 changes: 137 additions & 8 deletions errors/gerror/gerror_error_stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,164 @@ package gerror

import (
"bytes"
"container/list"
"fmt"
"runtime"
"strings"

"github.com/gogf/gf/v2/internal/consts"
)

// Stack returns the stack callers as string.
// It returns an empty string if the `err` does not support stacks.
// stackInfo manages stack info of certain error.
type stackInfo struct {
Index int // Index is the index of current error in whole error stacks.
Message string // Error information string.
Lines *list.List // Lines contains all error stack lines of current error stack in sequence.
}

// stackLine manages each line info of stack.
type stackLine struct {
Function string // Function name, which contains its full package path.
FileLine string // FileLine is the source file name and its line number of Function.
}

// Stack returns the error stack information as string.
func (err *Error) Stack() string {
if err == nil {
return ""
}
var (
loop = err
index = 1
buffer = bytes.NewBuffer(nil)
loop = err
index = 1
infos []*stackInfo
)
for loop != nil {
buffer.WriteString(fmt.Sprintf("%d. %-v\n", index, loop))
info := &stackInfo{
Index: index,
Message: fmt.Sprintf("%-v", loop),
}
index++
formatSubStack(loop.stack, buffer)
infos = append(infos, info)
loopLinesOfStackInfo(loop.stack, info)
if loop.error != nil {
if e, ok := loop.error.(*Error); ok {
loop = e
} else {
buffer.WriteString(fmt.Sprintf("%d. %s\n", index, loop.error.Error()))
infos = append(infos, &stackInfo{
Index: index,
Message: loop.error.Error(),
})
index++
break
}
} else {
break
}
}
filterLinesOfStackInfos(infos)
return formatStackInfos(infos)
}

// filterLinesOfStackInfos removes repeated lines, which exist in subsequent stacks, from top errors.
func filterLinesOfStackInfos(infos []*stackInfo) {
var (
ok bool
set = make(map[string]struct{})
info *stackInfo
line *stackLine
removes []*list.Element
)
for i := len(infos) - 1; i >= 0; i-- {
info = infos[i]
if info.Lines == nil {
continue
}
for n, e := 0, info.Lines.Front(); n < info.Lines.Len(); n, e = n+1, e.Next() {
line = e.Value.(*stackLine)
if _, ok = set[line.FileLine]; ok {
removes = append(removes, e)
} else {
set[line.FileLine] = struct{}{}
}
}
if len(removes) > 0 {
for _, e := range removes {
info.Lines.Remove(e)
}
}
removes = removes[:0]
}
}

// formatStackInfos formats and returns error stack information as string.
func formatStackInfos(infos []*stackInfo) string {
var buffer = bytes.NewBuffer(nil)
for i, info := range infos {
buffer.WriteString(fmt.Sprintf("%d. %s\n", i+1, info.Message))
if info.Lines != nil && info.Lines.Len() > 0 {
formatStackLines(buffer, info.Lines)
}
}
return buffer.String()
}

// formatStackLines formats and returns error stack lines as string.
func formatStackLines(buffer *bytes.Buffer, lines *list.List) string {
var (
line *stackLine
space = " "
length = lines.Len()
)
for i, e := 0, lines.Front(); i < length; i, e = i+1, e.Next() {
line = e.Value.(*stackLine)
// Graceful indent.
if i >= 9 {
space = " "
}
buffer.WriteString(fmt.Sprintf(
" %d).%s%s\n %s\n",
i+1, space, line.Function, line.FileLine,
))
}
return buffer.String()
}

// loopLinesOfStackInfo iterates the stack info lines and produces the stack line info.
func loopLinesOfStackInfo(st stack, info *stackInfo) {
if st == nil {
return
}
for _, p := range st {
if fn := runtime.FuncForPC(p - 1); fn != nil {
file, line := fn.FileLine(p - 1)
if isUsingBriefStack {
// filter whole GoFrame packages stack paths.
if strings.Contains(file, consts.StackFilterKeyForGoFrame) {
continue
}
} else {
// package path stack filtering.
if strings.Contains(file, stackFilterKeyLocal) {
continue
}
}
// Avoid stack string like "`autogenerated`"
if strings.Contains(file, "<") {
continue
}
// Ignore GO ROOT paths.
if goRootForFilter != "" &&
len(file) >= len(goRootForFilter) &&
file[0:len(goRootForFilter)] == goRootForFilter {
continue
}
if info.Lines == nil {
info.Lines = list.New()
}
info.Lines.PushBack(&stackLine{
Function: fn.Name(),
FileLine: fmt.Sprintf(`%s:%d`, file, line),
})
}
}
}