Skip to content

Commit

Permalink
Initial setup
Browse files Browse the repository at this point in the history
  • Loading branch information
Rahugg committed Nov 22, 2024
0 parents commit 33c90cf
Show file tree
Hide file tree
Showing 8 changed files with 515 additions and 0 deletions.
49 changes: 49 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Go CI

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
build:
name: Build and Test
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [ '1.22.x' ]

steps:
- uses: actions/checkout@v4

- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go-version }}
cache: true

- name: Install dependencies
run: go mod download

- name: Verify dependencies
run: go mod verify

- name: Run go vet
run: go vet ./...

- name: Run tests with coverage
run: go test ./... -v -race -coverprofile=coverage.txt -covermode=atomic

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

- name: Build
run: go build -v ./...

- name: Run golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: latest
10 changes: 10 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

115 changes: 115 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# go-stacktrace

`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.

## Features

- 🔍 Detailed stack traces for improved debugging
- 📝 Custom user messages and payload support
- 🎨 Colorized error output (configurable)
- 🔄 Error wrapping with context preservation
- 🪶 Lightweight with zero external dependencies
- 🧪 Comprehensive test coverage

## Installation

```bash
go get github.com/Rahugg/go-stacktrace
```

## Usage

### Basic Error Wrapping

```go
import "github.com/Rahugg/go-stacktrace/errorhandler"

func main() {
err := someFunction()
if err != nil {
wrappedErr := errorhandler.WrapError(
err,
"operation-failed", // payload
"Failed to process request" // user message
)
fmt.Println(errorhandler.FailOnError(wrappedErr))
}
}
```

### Controlling Color Output

```go
// Disable colored output
errorhandler.SetEnableColors(false)

// Enable colored output (default)
errorhandler.SetEnableColors(true)
```

### Error Information

The wrapped error includes:
- Original error message
- User-friendly message
- Custom payload (optional)
- Stack trace
- Color-coded output (optional)

## Example Output

```
Payload: operation-failed
Error Details
Original Error: file not found
User Message: Failed to process request
Stack Trace
main.someFunction
/path/to/file.go:25
main.main
/path/to/main.go:12
```

## Features in Detail

### TracedError Structure
```go
type TracedError struct {
OriginalError error
UserMessage string
Payload string
StackTrace []uintptr
}
```

### Available Colors
- Red: Error messages
- Green: User messages
- Yellow: Payload information
- Blue: Function names in stack trace
- Magenta: Stack trace headers
- Cyan: Section headers

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.
Or DM me.

## License

This project is licensed under the MIT License - see the LICENSE file for details.

## Project Structure
```
.
├── errorhandler
│ ├── colors.go # Color constants
│ ├── errorhandler.go # Core functionality
│ └── errorhandler_test.go # Test suite
├── example
│ └── main.go # Usage examples
├── go.mod
├── go.yml
└── README.md
```
35 changes: 35 additions & 0 deletions errorhandler/colors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package errorhandler

const (
// Reset sequence
Reset = "\033[0m"

// Black Regular colors
Black = "\033[30m"
Red = "\033[31m"
Green = "\033[32m"
Yellow = "\033[33m"
Blue = "\033[34m"
Magenta = "\033[35m"
Cyan = "\033[36m"
White = "\033[37m"

// BrightBlack Bright colors for better visibility
BrightBlack = "\033[90m"
BrightRed = "\033[91m"
BrightGreen = "\033[92m"
BrightYellow = "\033[93m"
BrightBlue = "\033[94m"
BrightMagenta = "\033[95m"
BrightCyan = "\033[96m"
BrightWhite = "\033[97m"

// BgRed Background colors (if needed)
BgRed = "\033[41m"
BgGreen = "\033[42m"
BgYellow = "\033[43m"
BgBlue = "\033[44m"
BgMagenta = "\033[45m"
BgCyan = "\033[46m"
BgWhite = "\033[47m"
)
157 changes: 157 additions & 0 deletions errorhandler/errorhandler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package errorhandler

import (
"errors"
"fmt"
"runtime"
"strings"
)

// EnableColors determines whether colored output is enabled.
var EnableColors = true

// TracedError represents an error with additional context, user-friendly messages, and stack trace information.
type TracedError struct {
OriginalError error
UserMessage string
Payload string
StackTrace []uintptr
}

// Error implements the error interface for TracedError, returning a formatted error message.
func (te *TracedError) Error() string {
return fmt.Sprintf("%s\nUser Message: %s", te.OriginalError.Error(), te.UserMessage)
}

// FailOnError formats the error message for any given error.
// If the error is a TracedError, it includes additional context.
func FailOnError(err error) string {
if err == nil {
return ""
}

var tracedErr *TracedError
if errors.As(err, &tracedErr) {
return tracedErr.formatError()
}

return err.Error()
}

// captureCallers retrieves the call stack up to a certain depth.
func captureCallers() []uintptr {
const stackDepth = 64
var pcs [stackDepth]uintptr
n := runtime.Callers(3, pcs[:])
return pcs[:n]
}

func formatWithColor(color, text string) string {
if EnableColors {
return fmt.Sprintf("%s%s%s", color, text, Reset)
}
return text
}

// formatError formats the TracedError for detailed display, including payload, user message, and stack trace.
// optionally using colors for better readability.
func (te *TracedError) formatError() string {
var sb strings.Builder

// Apply payload if available
if te.Payload != "" {
// Using BrightYellow for better visibility of metadata
sb.WriteString(fmt.Sprintf("%s: %s\n", formatWithColor(BrightYellow, "Payload"), te.Payload))
}

// Add error details header (using white for headers)
sb.WriteString(fmt.Sprintf("%s\n", formatWithColor(BrightWhite, "Error Details")))

// Original error in bright red for high visibility
sb.WriteString(
fmt.Sprintf(
"%s: %s\n",
formatWithColor(BrightRed, "Error"),
formatWithColor(Red, te.OriginalError.Error()),
),
)

// Add user message if available (using cyan for info)
if te.UserMessage != "" {
sb.WriteString(
fmt.Sprintf(
"%s: %s\n",
formatWithColor(BrightCyan, "User Message"),
formatWithColor(Cyan, te.UserMessage),
),
)
}

// Add stack trace header
sb.WriteString(fmt.Sprintf("\n%s\n", formatWithColor(BrightWhite, "Stack Trace")))
frames := runtime.CallersFrames(te.StackTrace)

for {
frame, more := frames.Next()
if !more {
break
}

// Skip runtime-related functions
if strings.Contains(frame.Function, "runtime.") {
continue
}

// Function name in bright blue for better visibility
sb.WriteString(fmt.Sprintf("%s\n", formatWithColor(BrightBlue, frame.Function)))
// File and line in dimmed white for less emphasis
sb.WriteString(
fmt.Sprintf(
"\t%s:%d\n",
formatWithColor(White, frame.File),
frame.Line,
),
)
}

return sb.String()
}

// WrapError wraps an existing error with additional context, including payload and a user-friendly message.
func WrapError(err error, payload, message string) error {
if err == nil {
return nil
}

var existingTracedErr *TracedError
if errors.As(err, &existingTracedErr) {
// Merge existing TracedError context
if payload == "" {
payload = existingTracedErr.Payload
}
if message != "" && existingTracedErr.UserMessage != "" {
message = fmt.Sprintf("%s\n%s", existingTracedErr.UserMessage, message)
} else if message == "" {
message = existingTracedErr.UserMessage
}
return &TracedError{
OriginalError: existingTracedErr.OriginalError,
UserMessage: message,
Payload: payload,
StackTrace: captureCallers(),
}
}

// Create a new TracedError
return &TracedError{
OriginalError: err,
UserMessage: message,
Payload: payload,
StackTrace: captureCallers(),
}
}

// SetEnableColors sets the global EnableColors flag for controlling colored output.
func SetEnableColors(enable bool) {
EnableColors = enable
}
Loading

0 comments on commit 33c90cf

Please sign in to comment.