277 lines
8.6 KiB
Go
277 lines
8.6 KiB
Go
package errors
|
|
|
|
import (
|
|
"fmt"
|
|
"runtime"
|
|
"time"
|
|
)
|
|
|
|
// ErrorCategory represents the category of an error
|
|
type ErrorCategory string
|
|
|
|
const (
|
|
CategoryNetwork ErrorCategory = "NETWORK" // RPC, DNS, connection issues
|
|
CategoryParsing ErrorCategory = "PARSING" // ABI decoding, transaction parsing
|
|
CategoryValidation ErrorCategory = "VALIDATION" // Input validation, data validation
|
|
CategoryExecution ErrorCategory = "EXECUTION" // Transaction execution, contract calls
|
|
CategoryConfiguration ErrorCategory = "CONFIGURATION" // Config loading, invalid settings
|
|
CategoryDatabase ErrorCategory = "DATABASE" // Database operations
|
|
CategorySecurity ErrorCategory = "SECURITY" // Security violations, unauthorized access
|
|
CategoryMath ErrorCategory = "MATH" // Arithmetic errors, overflow/underflow
|
|
CategoryInternal ErrorCategory = "INTERNAL" // Internal logic errors, unexpected state
|
|
CategoryExternal ErrorCategory = "EXTERNAL" // External service failures
|
|
CategoryUnknown ErrorCategory = "UNKNOWN" // Uncategorized errors
|
|
)
|
|
|
|
// ErrorSeverity represents how critical an error is
|
|
type ErrorSeverity string
|
|
|
|
const (
|
|
SeverityDebug ErrorSeverity = "DEBUG" // Informational, not an actual error
|
|
SeverityInfo ErrorSeverity = "INFO" // Notable but not problematic
|
|
SeverityWarning ErrorSeverity = "WARNING" // Potential issue, should investigate
|
|
SeverityError ErrorSeverity = "ERROR" // Actual error, functionality impacted
|
|
SeverityCritical ErrorSeverity = "CRITICAL" // Critical error, immediate attention required
|
|
SeverityFatal ErrorSeverity = "FATAL" // Fatal error, system cannot continue
|
|
)
|
|
|
|
// StructuredError represents a comprehensive error with full context
|
|
type StructuredError struct {
|
|
// Core error information
|
|
Message string // Human-readable error message
|
|
Category ErrorCategory // Error category for classification
|
|
Severity ErrorSeverity // How critical is this error
|
|
|
|
// Origin tracking
|
|
File string // Source file where error occurred
|
|
Function string // Function where error occurred
|
|
Line int // Line number where error occurred
|
|
Package string // Go package where error occurred
|
|
|
|
// Context
|
|
Reason string // Why this error occurred (root cause)
|
|
Action string // What the code was trying to do when it failed
|
|
Impact string // Impact of this error on the system
|
|
Suggestion string // Suggested fix or next steps
|
|
Details map[string]interface{} // Additional structured context
|
|
UnderlyingErr error // Original error if wrapping
|
|
|
|
// Metadata
|
|
Timestamp time.Time // When the error occurred
|
|
ErrorID string // Unique identifier for this error instance
|
|
}
|
|
|
|
// Error implements the error interface
|
|
func (e *StructuredError) Error() string {
|
|
if e.UnderlyingErr != nil {
|
|
return fmt.Sprintf("[%s/%s] %s: %v (in %s:%d)",
|
|
e.Category, e.Severity, e.Message, e.UnderlyingErr, e.File, e.Line)
|
|
}
|
|
return fmt.Sprintf("[%s/%s] %s (in %s:%d)",
|
|
e.Category, e.Severity, e.Message, e.File, e.Line)
|
|
}
|
|
|
|
// Unwrap returns the underlying error for error chain support
|
|
func (e *StructuredError) Unwrap() error {
|
|
return e.UnderlyingErr
|
|
}
|
|
|
|
// NewStructuredError creates a new structured error with automatic caller tracking
|
|
func NewStructuredError(category ErrorCategory, severity ErrorSeverity, message string) *StructuredError {
|
|
return newStructuredErrorWithDepth(category, severity, message, 2)
|
|
}
|
|
|
|
// newStructuredErrorWithDepth creates a structured error with custom stack depth
|
|
func newStructuredErrorWithDepth(category ErrorCategory, severity ErrorSeverity, message string, depth int) *StructuredError {
|
|
// Get caller information
|
|
pc, file, line, ok := runtime.Caller(depth)
|
|
if !ok {
|
|
file = "unknown"
|
|
line = 0
|
|
}
|
|
|
|
funcName := "unknown"
|
|
packageName := "unknown"
|
|
if fn := runtime.FuncForPC(pc); fn != nil {
|
|
funcName = fn.Name()
|
|
// Extract package name from function name (format: "package/path.FunctionName")
|
|
for i := len(funcName) - 1; i >= 0; i-- {
|
|
if funcName[i] == '/' {
|
|
packageName = funcName[:i]
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate unique error ID
|
|
errorID := fmt.Sprintf("ERR-%d-%s", time.Now().UnixNano(), category)
|
|
|
|
return &StructuredError{
|
|
Message: message,
|
|
Category: category,
|
|
Severity: severity,
|
|
File: file,
|
|
Function: funcName,
|
|
Line: line,
|
|
Package: packageName,
|
|
Details: make(map[string]interface{}),
|
|
Timestamp: time.Now(),
|
|
ErrorID: errorID,
|
|
}
|
|
}
|
|
|
|
// WithReason adds the reason (root cause) for the error
|
|
func (e *StructuredError) WithReason(reason string) *StructuredError {
|
|
e.Reason = reason
|
|
return e
|
|
}
|
|
|
|
// WithAction describes what the code was trying to do
|
|
func (e *StructuredError) WithAction(action string) *StructuredError {
|
|
e.Action = action
|
|
return e
|
|
}
|
|
|
|
// WithImpact describes the impact of this error
|
|
func (e *StructuredError) WithImpact(impact string) *StructuredError {
|
|
e.Impact = impact
|
|
return e
|
|
}
|
|
|
|
// WithSuggestion provides a suggestion for fixing or handling the error
|
|
func (e *StructuredError) WithSuggestion(suggestion string) *StructuredError {
|
|
e.Suggestion = suggestion
|
|
return e
|
|
}
|
|
|
|
// WithDetail adds a key-value detail to the error context
|
|
func (e *StructuredError) WithDetail(key string, value interface{}) *StructuredError {
|
|
e.Details[key] = value
|
|
return e
|
|
}
|
|
|
|
// WithDetails adds multiple details at once
|
|
func (e *StructuredError) WithDetails(details map[string]interface{}) *StructuredError {
|
|
for k, v := range details {
|
|
e.Details[k] = v
|
|
}
|
|
return e
|
|
}
|
|
|
|
// Wrap wraps an underlying error with structured context
|
|
func (e *StructuredError) Wrap(err error) *StructuredError {
|
|
e.UnderlyingErr = err
|
|
return e
|
|
}
|
|
|
|
// FormatForLogging returns a comprehensive string representation for logging
|
|
func (e *StructuredError) FormatForLogging() string {
|
|
result := fmt.Sprintf(
|
|
"[%s] %s/%s: %s\n"+
|
|
" Origin: %s:%d (%s)\n"+
|
|
" ErrorID: %s\n"+
|
|
" Timestamp: %s\n",
|
|
e.ErrorID,
|
|
e.Category,
|
|
e.Severity,
|
|
e.Message,
|
|
e.File,
|
|
e.Line,
|
|
e.Function,
|
|
e.ErrorID,
|
|
e.Timestamp.Format(time.RFC3339),
|
|
)
|
|
|
|
if e.Reason != "" {
|
|
result += fmt.Sprintf(" Reason: %s\n", e.Reason)
|
|
}
|
|
|
|
if e.Action != "" {
|
|
result += fmt.Sprintf(" Action: %s\n", e.Action)
|
|
}
|
|
|
|
if e.Impact != "" {
|
|
result += fmt.Sprintf(" Impact: %s\n", e.Impact)
|
|
}
|
|
|
|
if e.Suggestion != "" {
|
|
result += fmt.Sprintf(" Suggestion: %s\n", e.Suggestion)
|
|
}
|
|
|
|
if len(e.Details) > 0 {
|
|
result += " Details:\n"
|
|
for k, v := range e.Details {
|
|
result += fmt.Sprintf(" - %s: %v\n", k, v)
|
|
}
|
|
}
|
|
|
|
if e.UnderlyingErr != nil {
|
|
result += fmt.Sprintf(" Underlying: %v\n", e.UnderlyingErr)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// FormatCompact returns a single-line representation
|
|
func (e *StructuredError) FormatCompact() string {
|
|
compact := fmt.Sprintf("[%s/%s] %s", e.Category, e.Severity, e.Message)
|
|
|
|
if e.Reason != "" {
|
|
compact += fmt.Sprintf(" | Reason: %s", e.Reason)
|
|
}
|
|
|
|
if e.Action != "" {
|
|
compact += fmt.Sprintf(" | Action: %s", e.Action)
|
|
}
|
|
|
|
compact += fmt.Sprintf(" | Origin: %s:%d", e.File, e.Line)
|
|
|
|
if e.UnderlyingErr != nil {
|
|
compact += fmt.Sprintf(" | Underlying: %v", e.UnderlyingErr)
|
|
}
|
|
|
|
return compact
|
|
}
|
|
|
|
// Helper functions for common error patterns
|
|
|
|
// NetworkError creates a network-related error
|
|
func NetworkError(message string) *StructuredError {
|
|
return newStructuredErrorWithDepth(CategoryNetwork, SeverityError, message, 2)
|
|
}
|
|
|
|
// ParsingError creates a parsing-related error
|
|
func ParsingError(message string) *StructuredError {
|
|
return newStructuredErrorWithDepth(CategoryParsing, SeverityError, message, 2)
|
|
}
|
|
|
|
// ValidationError creates a validation-related error
|
|
func ValidationError(message string) *StructuredError {
|
|
return newStructuredErrorWithDepth(CategoryValidation, SeverityWarning, message, 2)
|
|
}
|
|
|
|
// ExecutionError creates an execution-related error
|
|
func ExecutionError(message string) *StructuredError {
|
|
return newStructuredErrorWithDepth(CategoryExecution, SeverityCritical, message, 2)
|
|
}
|
|
|
|
// ConfigurationError creates a configuration-related error
|
|
func ConfigurationError(message string) *StructuredError {
|
|
return newStructuredErrorWithDepth(CategoryConfiguration, SeverityCritical, message, 2)
|
|
}
|
|
|
|
// InternalError creates an internal logic error
|
|
func InternalError(message string) *StructuredError {
|
|
return newStructuredErrorWithDepth(CategoryInternal, SeverityError, message, 2)
|
|
}
|
|
|
|
// MathError creates a mathematical error
|
|
func MathError(message string) *StructuredError {
|
|
return newStructuredErrorWithDepth(CategoryMath, SeverityError, message, 2)
|
|
}
|
|
|
|
// SecurityError creates a security-related error
|
|
func SecurityError(message string) *StructuredError {
|
|
return newStructuredErrorWithDepth(CategorySecurity, SeverityCritical, message, 2)
|
|
}
|