Skip to content

Commit

Permalink
feat: add variable state capture to error logging
Browse files Browse the repository at this point in the history
  • Loading branch information
aliraza556 committed Dec 19, 2024
1 parent efdcbc9 commit bd631fb
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 12 deletions.
8 changes: 5 additions & 3 deletions routes/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"github.com/stakwork/sphinx-tribes/utils"
)


// NewRouter creates a chi router
func NewRouter() *http.Server {
r := initChi()
Expand Down Expand Up @@ -203,14 +202,17 @@ func internalServerErrorHandler(next http.Handler) http.Handler {
n := runtime.Stack(buf, true)
stackTrace := string(buf[:n])

state := utils.CaptureVariableState(err)

// Format stack trace to edge list
edgeList := utils.FormatStacktraceToEdgeList(stackTrace, err)
edgeList := utils.FormatStacktraceToEdgeList(stackTrace, err, state)

utils.Log.Error("Internal Server Error: %s %s\nError: %v\nStack Trace:\n%s\nEdge List:\n%+v\n",
utils.Log.Error("Internal Server Error: %s %s\nError: %v\nStack Trace:\n%s\nVariable State:\n%s\nEdge List:\n%+v\n",
r.Method,
r.URL.Path,
err,
stackTrace,
state,
utils.PrettyPrintEdgeList(edgeList),
)

Expand Down
23 changes: 20 additions & 3 deletions utils/stacktrace.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,24 @@ type TraceNodeData struct {
TraceUUID string `json:"trace_uuid"`
}

func FormatStacktraceToEdgeList(stackTrace string, err interface{}) EdgeList {
func CaptureVariableState(err interface{}) string {

errStr := fmt.Sprintf("%+v", err)

var state strings.Builder
state.WriteString("Error Variables:\n")

if m, ok := err.(map[string]interface{}); ok {
b, _ := json.MarshalIndent(m, "", " ")
state.Write(b)
} else {
state.WriteString(errStr)
}

return state.String()
}

func FormatStacktraceToEdgeList(stackTrace string, err interface{}, state string) EdgeList {

now := time.Now().Unix()

Expand All @@ -81,7 +98,7 @@ func FormatStacktraceToEdgeList(stackTrace string, err interface{}) EdgeList {

reportNodeData := ReportNodeData{
AppType: nil,
Errors: fmt.Sprintf("%v", err),
Errors: fmt.Sprintf("%v\nVariable State:\n%s", err, state),
ReleaseStage: "development",
ReportID: reportID,
SeverityReason: "unhandledException",
Expand Down Expand Up @@ -266,7 +283,7 @@ func parseStackTrace(stackTrace string) []string {
}
}
if len(frames) > 1 {
return frames[2:]
return frames[2:]
}
return frames
}
Expand Down
59 changes: 53 additions & 6 deletions utils/stacktrace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ func TestFormatStacktraceToEdgeList(t *testing.T) {
name string
stackTrace string
err interface{}
state string
expectedChecks func(t *testing.T, edgeList EdgeList)
}{
{
name: "Basic Error Scenario",
stackTrace: generateTestStackTrace(),
err: fmt.Errorf("test error"),
state: "Error Variables:\ntest error",
expectedChecks: func(t *testing.T, edgeList EdgeList) {
assert.NotNil(t, edgeList)
assert.Greater(t, len(edgeList.EdgeList), 0)
Expand All @@ -41,6 +43,12 @@ func TestFormatStacktraceToEdgeList(t *testing.T) {
assert.Equal(t, "Report", generatedByEdge.Source.NodeType)
assert.Equal(t, "Application", generatedByEdge.Targets[0].NodeType)

// Verify error message contains state information
reportNode := generatedByEdge.Source
reportData, ok := reportNode.NodeData.(ReportNodeData)
assert.True(t, ok)
assert.Contains(t, reportData.Errors, "Variable State:")

// Check HAS edges
hasEdges := findEdgesByType(edgeList, "HAS")
assert.Greater(t, len(hasEdges), 0)
Expand All @@ -62,6 +70,7 @@ func TestFormatStacktraceToEdgeList(t *testing.T) {
name: "Nil Error Scenario",
stackTrace: generateTestStackTrace(),
err: nil,
state: "Error Variables:\n<nil>",
expectedChecks: func(t *testing.T, edgeList EdgeList) {
assert.NotNil(t, edgeList)
assert.Greater(t, len(edgeList.EdgeList), 0)
Expand All @@ -71,6 +80,7 @@ func TestFormatStacktraceToEdgeList(t *testing.T) {
name: "Complex Error Scenario",
stackTrace: generateTestStackTrace(),
err: struct{ message string }{"complex error"},
state: "Error Variables:\n{message:complex error}",
expectedChecks: func(t *testing.T, edgeList EdgeList) {
assert.NotNil(t, edgeList)
assert.Greater(t, len(edgeList.EdgeList), 0)
Expand All @@ -80,7 +90,7 @@ func TestFormatStacktraceToEdgeList(t *testing.T) {

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
edgeList := FormatStacktraceToEdgeList(tc.stackTrace, tc.err)
edgeList := FormatStacktraceToEdgeList(tc.stackTrace, tc.err, tc.state)
tc.expectedChecks(t, edgeList)
})
}
Expand Down Expand Up @@ -119,6 +129,42 @@ another random line`,
}
}

func TestCaptureVariableState(t *testing.T) {
testCases := []struct {
name string
err interface{}
expectedState string
}{
{
name: "Simple Error",
err: fmt.Errorf("test error"),
expectedState: "Error Variables:\ntest error",
},
{
name: "Map Error",
err: map[string]interface{}{"key": "value"},
expectedState: "Error Variables:\n{\n \"key\": \"value\"\n}",
},
{
name: "Nil Error",
err: nil,
expectedState: "Error Variables:\n<nil>",
},
{
name: "Struct Error",
err: struct{ msg string }{"test"},
expectedState: "Error Variables:\n{msg:test}",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
state := CaptureVariableState(tc.err)
assert.Equal(t, tc.expectedState, state)
})
}
}

func TestGenerateReportID(t *testing.T) {

reportID1 := generateReportID()
Expand Down Expand Up @@ -151,38 +197,39 @@ func findEdgesByType(edgeList EdgeList, edgeType string) []Edge {
func BenchmarkFormatStacktraceToEdgeList(b *testing.B) {
stackTrace := generateTestStackTrace()
err := fmt.Errorf("benchmark error")
state := CaptureVariableState(err)

b.ResetTimer()
for i := 0; i < b.N; i++ {
FormatStacktraceToEdgeList(stackTrace, err)
FormatStacktraceToEdgeList(stackTrace, err, state)
}
}

func TestLongStackTraceHandling(t *testing.T) {

var longStackTraceBuilder strings.Builder
for i := 0; i < 1000; i++ {
longStackTraceBuilder.WriteString(fmt.Sprintf("goroutine %d [running]:\n", i))
longStackTraceBuilder.WriteString(fmt.Sprintf("/path/to/long/stack/trace%d.go:%d +0x26\n", i, i))
}
longStackTrace := longStackTraceBuilder.String()
state := "Error Variables:\nStress Test Error"

edgeList := FormatStacktraceToEdgeList(longStackTrace, "Stress Test Error")
edgeList := FormatStacktraceToEdgeList(longStackTrace, "Stress Test Error", state)
assert.NotNil(t, edgeList)
assert.Greater(t, len(edgeList.EdgeList), 0)
}

func TestConcurrentStackTraceFormatting(t *testing.T) {
stackTrace := generateTestStackTrace()
err := fmt.Errorf("concurrent error")
state := CaptureVariableState(err)

concurrentRuns := 100

results := make(chan EdgeList, concurrentRuns)

for i := 0; i < concurrentRuns; i++ {
go func() {
results <- FormatStacktraceToEdgeList(stackTrace, err)
results <- FormatStacktraceToEdgeList(stackTrace, err, state)
}()
}

Expand Down

0 comments on commit bd631fb

Please sign in to comment.