Skip to content

Commit 33c90cf

Browse files
author
Rahugg
committed
Initial setup
0 parents  commit 33c90cf

File tree

8 files changed

+515
-0
lines changed

8 files changed

+515
-0
lines changed

.github/workflows/go.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Go CI
2+
3+
on:
4+
push:
5+
branches: [ master ]
6+
pull_request:
7+
branches: [ master ]
8+
9+
jobs:
10+
build:
11+
name: Build and Test
12+
runs-on: ubuntu-latest
13+
strategy:
14+
matrix:
15+
go-version: [ '1.22.x' ]
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Set up Go ${{ matrix.go-version }}
21+
uses: actions/setup-go@v4
22+
with:
23+
go-version: ${{ matrix.go-version }}
24+
cache: true
25+
26+
- name: Install dependencies
27+
run: go mod download
28+
29+
- name: Verify dependencies
30+
run: go mod verify
31+
32+
- name: Run go vet
33+
run: go vet ./...
34+
35+
- name: Run tests with coverage
36+
run: go test ./... -v -race -coverprofile=coverage.txt -covermode=atomic
37+
38+
- name: Upload coverage reports to Codecov
39+
uses: codecov/codecov-action@v3
40+
env:
41+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
42+
43+
- name: Build
44+
run: go build -v ./...
45+
46+
- name: Run golangci-lint
47+
uses: golangci/golangci-lint-action@v3
48+
with:
49+
version: latest

.idea/.gitignore

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Readme.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# go-stacktrace
2+
3+
`go-stacktrace` is a robust Go package for enhanced error handling, providing stack traces, error wrapping, and user-friendly error messages with optional color formatting.
4+
5+
## Features
6+
7+
- 🔍 Detailed stack traces for improved debugging
8+
- 📝 Custom user messages and payload support
9+
- 🎨 Colorized error output (configurable)
10+
- 🔄 Error wrapping with context preservation
11+
- 🪶 Lightweight with zero external dependencies
12+
- 🧪 Comprehensive test coverage
13+
14+
## Installation
15+
16+
```bash
17+
go get github.com/Rahugg/go-stacktrace
18+
```
19+
20+
## Usage
21+
22+
### Basic Error Wrapping
23+
24+
```go
25+
import "github.com/Rahugg/go-stacktrace/errorhandler"
26+
27+
func main() {
28+
err := someFunction()
29+
if err != nil {
30+
wrappedErr := errorhandler.WrapError(
31+
err,
32+
"operation-failed", // payload
33+
"Failed to process request" // user message
34+
)
35+
fmt.Println(errorhandler.FailOnError(wrappedErr))
36+
}
37+
}
38+
```
39+
40+
### Controlling Color Output
41+
42+
```go
43+
// Disable colored output
44+
errorhandler.SetEnableColors(false)
45+
46+
// Enable colored output (default)
47+
errorhandler.SetEnableColors(true)
48+
```
49+
50+
### Error Information
51+
52+
The wrapped error includes:
53+
- Original error message
54+
- User-friendly message
55+
- Custom payload (optional)
56+
- Stack trace
57+
- Color-coded output (optional)
58+
59+
## Example Output
60+
61+
```
62+
Payload: operation-failed
63+
Error Details
64+
Original Error: file not found
65+
User Message: Failed to process request
66+
67+
Stack Trace
68+
main.someFunction
69+
/path/to/file.go:25
70+
main.main
71+
/path/to/main.go:12
72+
```
73+
74+
## Features in Detail
75+
76+
### TracedError Structure
77+
```go
78+
type TracedError struct {
79+
OriginalError error
80+
UserMessage string
81+
Payload string
82+
StackTrace []uintptr
83+
}
84+
```
85+
86+
### Available Colors
87+
- Red: Error messages
88+
- Green: User messages
89+
- Yellow: Payload information
90+
- Blue: Function names in stack trace
91+
- Magenta: Stack trace headers
92+
- Cyan: Section headers
93+
94+
## Contributing
95+
96+
Contributions are welcome! Please feel free to submit a Pull Request.
97+
Or DM me.
98+
99+
## License
100+
101+
This project is licensed under the MIT License - see the LICENSE file for details.
102+
103+
## Project Structure
104+
```
105+
.
106+
├── errorhandler
107+
│ ├── colors.go # Color constants
108+
│ ├── errorhandler.go # Core functionality
109+
│ └── errorhandler_test.go # Test suite
110+
├── example
111+
│ └── main.go # Usage examples
112+
├── go.mod
113+
├── go.yml
114+
└── README.md
115+
```

errorhandler/colors.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package errorhandler
2+
3+
const (
4+
// Reset sequence
5+
Reset = "\033[0m"
6+
7+
// Black Regular colors
8+
Black = "\033[30m"
9+
Red = "\033[31m"
10+
Green = "\033[32m"
11+
Yellow = "\033[33m"
12+
Blue = "\033[34m"
13+
Magenta = "\033[35m"
14+
Cyan = "\033[36m"
15+
White = "\033[37m"
16+
17+
// BrightBlack Bright colors for better visibility
18+
BrightBlack = "\033[90m"
19+
BrightRed = "\033[91m"
20+
BrightGreen = "\033[92m"
21+
BrightYellow = "\033[93m"
22+
BrightBlue = "\033[94m"
23+
BrightMagenta = "\033[95m"
24+
BrightCyan = "\033[96m"
25+
BrightWhite = "\033[97m"
26+
27+
// BgRed Background colors (if needed)
28+
BgRed = "\033[41m"
29+
BgGreen = "\033[42m"
30+
BgYellow = "\033[43m"
31+
BgBlue = "\033[44m"
32+
BgMagenta = "\033[45m"
33+
BgCyan = "\033[46m"
34+
BgWhite = "\033[47m"
35+
)

errorhandler/errorhandler.go

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package errorhandler
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"runtime"
7+
"strings"
8+
)
9+
10+
// EnableColors determines whether colored output is enabled.
11+
var EnableColors = true
12+
13+
// TracedError represents an error with additional context, user-friendly messages, and stack trace information.
14+
type TracedError struct {
15+
OriginalError error
16+
UserMessage string
17+
Payload string
18+
StackTrace []uintptr
19+
}
20+
21+
// Error implements the error interface for TracedError, returning a formatted error message.
22+
func (te *TracedError) Error() string {
23+
return fmt.Sprintf("%s\nUser Message: %s", te.OriginalError.Error(), te.UserMessage)
24+
}
25+
26+
// FailOnError formats the error message for any given error.
27+
// If the error is a TracedError, it includes additional context.
28+
func FailOnError(err error) string {
29+
if err == nil {
30+
return ""
31+
}
32+
33+
var tracedErr *TracedError
34+
if errors.As(err, &tracedErr) {
35+
return tracedErr.formatError()
36+
}
37+
38+
return err.Error()
39+
}
40+
41+
// captureCallers retrieves the call stack up to a certain depth.
42+
func captureCallers() []uintptr {
43+
const stackDepth = 64
44+
var pcs [stackDepth]uintptr
45+
n := runtime.Callers(3, pcs[:])
46+
return pcs[:n]
47+
}
48+
49+
func formatWithColor(color, text string) string {
50+
if EnableColors {
51+
return fmt.Sprintf("%s%s%s", color, text, Reset)
52+
}
53+
return text
54+
}
55+
56+
// formatError formats the TracedError for detailed display, including payload, user message, and stack trace.
57+
// optionally using colors for better readability.
58+
func (te *TracedError) formatError() string {
59+
var sb strings.Builder
60+
61+
// Apply payload if available
62+
if te.Payload != "" {
63+
// Using BrightYellow for better visibility of metadata
64+
sb.WriteString(fmt.Sprintf("%s: %s\n", formatWithColor(BrightYellow, "Payload"), te.Payload))
65+
}
66+
67+
// Add error details header (using white for headers)
68+
sb.WriteString(fmt.Sprintf("%s\n", formatWithColor(BrightWhite, "Error Details")))
69+
70+
// Original error in bright red for high visibility
71+
sb.WriteString(
72+
fmt.Sprintf(
73+
"%s: %s\n",
74+
formatWithColor(BrightRed, "Error"),
75+
formatWithColor(Red, te.OriginalError.Error()),
76+
),
77+
)
78+
79+
// Add user message if available (using cyan for info)
80+
if te.UserMessage != "" {
81+
sb.WriteString(
82+
fmt.Sprintf(
83+
"%s: %s\n",
84+
formatWithColor(BrightCyan, "User Message"),
85+
formatWithColor(Cyan, te.UserMessage),
86+
),
87+
)
88+
}
89+
90+
// Add stack trace header
91+
sb.WriteString(fmt.Sprintf("\n%s\n", formatWithColor(BrightWhite, "Stack Trace")))
92+
frames := runtime.CallersFrames(te.StackTrace)
93+
94+
for {
95+
frame, more := frames.Next()
96+
if !more {
97+
break
98+
}
99+
100+
// Skip runtime-related functions
101+
if strings.Contains(frame.Function, "runtime.") {
102+
continue
103+
}
104+
105+
// Function name in bright blue for better visibility
106+
sb.WriteString(fmt.Sprintf("%s\n", formatWithColor(BrightBlue, frame.Function)))
107+
// File and line in dimmed white for less emphasis
108+
sb.WriteString(
109+
fmt.Sprintf(
110+
"\t%s:%d\n",
111+
formatWithColor(White, frame.File),
112+
frame.Line,
113+
),
114+
)
115+
}
116+
117+
return sb.String()
118+
}
119+
120+
// WrapError wraps an existing error with additional context, including payload and a user-friendly message.
121+
func WrapError(err error, payload, message string) error {
122+
if err == nil {
123+
return nil
124+
}
125+
126+
var existingTracedErr *TracedError
127+
if errors.As(err, &existingTracedErr) {
128+
// Merge existing TracedError context
129+
if payload == "" {
130+
payload = existingTracedErr.Payload
131+
}
132+
if message != "" && existingTracedErr.UserMessage != "" {
133+
message = fmt.Sprintf("%s\n%s", existingTracedErr.UserMessage, message)
134+
} else if message == "" {
135+
message = existingTracedErr.UserMessage
136+
}
137+
return &TracedError{
138+
OriginalError: existingTracedErr.OriginalError,
139+
UserMessage: message,
140+
Payload: payload,
141+
StackTrace: captureCallers(),
142+
}
143+
}
144+
145+
// Create a new TracedError
146+
return &TracedError{
147+
OriginalError: err,
148+
UserMessage: message,
149+
Payload: payload,
150+
StackTrace: captureCallers(),
151+
}
152+
}
153+
154+
// SetEnableColors sets the global EnableColors flag for controlling colored output.
155+
func SetEnableColors(enable bool) {
156+
EnableColors = enable
157+
}

0 commit comments

Comments
 (0)