Skip to content

Commit 165b098

Browse files
committed
add GetTraces() and slog helper
1 parent 30050ab commit 165b098

File tree

6 files changed

+378
-199
lines changed

6 files changed

+378
-199
lines changed

slogex.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package stacktrace
2+
3+
import (
4+
"fmt"
5+
"log/slog"
6+
)
7+
8+
func ErrToSlogAttr(err error, opts ...TracesOpt) slog.Attr {
9+
if err == nil {
10+
return slog.Attr{}
11+
}
12+
13+
st, ok := Unwrap(err)
14+
if !ok {
15+
return slog.String("error", err.Error())
16+
}
17+
18+
if st == nil {
19+
return slog.String("error", "nil stacktrace")
20+
}
21+
22+
tracebacks := st.GetTraces(opts...)
23+
24+
tracebackAttrs := make([]slog.Attr, 0, len(tracebacks))
25+
for traceIndex := range tracebacks {
26+
traceback := tracebacks[traceIndex]
27+
stackAttrs := make([]slog.Attr, 0, len(traceback.Stack))
28+
for stackIndex := range traceback.Stack {
29+
stack := traceback.Stack[stackIndex]
30+
key := fmt.Sprintf("%d", stackIndex)
31+
stackAttr := slog.Group(
32+
key,
33+
slog.String("type", string(stack.Type)),
34+
slog.String("severity", string(stack.Severity)),
35+
slog.String("position", stack.LinePos),
36+
slog.String("message", stack.Message),
37+
)
38+
stackAttrs = append(stackAttrs, stackAttr)
39+
}
40+
tracebackAttr := slog.Group(fmt.Sprintf("%d", traceIndex), "stack", stackAttrs)
41+
tracebackAttrs = append(tracebackAttrs, tracebackAttr)
42+
}
43+
44+
return slog.Group("tracebacks", "traces", tracebackAttrs)
45+
}

sprint.go

Lines changed: 0 additions & 123 deletions
This file was deleted.

sprint_test.go

Lines changed: 0 additions & 70 deletions
This file was deleted.

stacktrace.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -185,13 +185,8 @@ func (st *StackTrace) Header() string {
185185
result := fmt.Sprintf("[%s] %s: %s",
186186
st.Severity,
187187
st.Type,
188-
st.Location,
188+
st.GetLocWithPos(),
189189
)
190-
if st.Position != nil {
191-
result = fmt.Sprintf("%s:%d:%d", result, st.Position.Line, st.Position.Column)
192-
} else {
193-
result = fmt.Sprintf("%s:1", result)
194-
}
195190
return result
196191
}
197192

@@ -442,6 +437,17 @@ func (st *StackTrace) Clone() *StackTrace {
442437
return &c
443438
}
444439

440+
// GetLocWithPos returns the location with position of the StackTrace.
441+
func (st *StackTrace) GetLocWithPos() string {
442+
result := st.Location
443+
if st.Position != nil {
444+
result = fmt.Sprintf("%s:%d:%d", result, st.Position.Line, st.Position.Column)
445+
} else {
446+
result = fmt.Sprintf("%s:1", result)
447+
}
448+
return result
449+
}
450+
445451
// Position contains the line and column where the error occurred.
446452
type Position struct {
447453
Line int

traces.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package stacktrace
2+
3+
type TracesOpt interface {
4+
Apply(o *TracesOptions)
5+
}
6+
7+
type TracesOptions struct {
8+
// EnsureDuplicates ensures that duplicates are not printed
9+
EnsureDuplicates bool
10+
dupLocs map[string]struct{}
11+
}
12+
13+
func NewTracesOptions() *TracesOptions {
14+
opts := &TracesOptions{
15+
EnsureDuplicates: false,
16+
dupLocs: make(map[string]struct{}),
17+
}
18+
return opts
19+
}
20+
21+
type ensureDuplicatesOpt struct{}
22+
23+
func (ensureDuplicatesOpt) Apply(o *TracesOptions) {
24+
o.EnsureDuplicates = true
25+
}
26+
27+
func WithEnsureDuplicates() TracesOpt {
28+
return &ensureDuplicatesOpt{}
29+
}
30+
31+
type Stack struct {
32+
LinePos string
33+
Severity Severity
34+
Message string
35+
Type Type
36+
}
37+
38+
func NewStack() *Stack {
39+
return &Stack{}
40+
}
41+
42+
type Trace struct {
43+
Stack []Stack
44+
}
45+
46+
func NewTrace() *Trace {
47+
return &Trace{Stack: make([]Stack, 0)}
48+
}
49+
50+
func (st *StackTrace) getTraces(opts *TracesOptions) []Trace {
51+
traces := make([]Trace, 0)
52+
53+
tracesWithList := func() []Trace {
54+
for _, elem := range st.List {
55+
elemTraces := elem.getTraces(opts)
56+
traces = append(traces, elemTraces...)
57+
}
58+
return traces
59+
}
60+
61+
trace := NewTrace()
62+
stack := NewStack()
63+
stack.LinePos = st.GetLocWithPos()
64+
stack.Severity = st.Severity
65+
stack.Message = st.FullMessageWithInfo()
66+
stack.Type = st.Type
67+
68+
if _, ok := opts.dupLocs[stack.LinePos]; ok {
69+
return tracesWithList()
70+
}
71+
72+
trace.Stack = append(trace.Stack, *stack)
73+
if st.Wrapped != nil {
74+
wrappedTraces := st.Wrapped.getTraces(opts)
75+
if len(wrappedTraces) == 0 {
76+
return tracesWithList()
77+
}
78+
for i := range wrappedTraces {
79+
trace.Stack = append(trace.Stack, wrappedTraces[i].Stack...)
80+
}
81+
} else if opts.EnsureDuplicates {
82+
opts.dupLocs[stack.LinePos] = struct{}{}
83+
}
84+
85+
traces = append(traces, *trace)
86+
87+
return tracesWithList()
88+
}
89+
90+
func (st *StackTrace) GetTraces(opts ...TracesOpt) []Trace {
91+
o := NewTracesOptions()
92+
for _, opt := range opts {
93+
opt.Apply(o)
94+
}
95+
return st.getTraces(o)
96+
}

0 commit comments

Comments
 (0)