Skip to content

Commit 5556c65

Browse files
committed
docs: add usage examples for error handling functions and builder pattern in goerr package to enhance documentation clarity
1 parent 0e42168 commit 5556c65

File tree

6 files changed

+447
-0
lines changed

6 files changed

+447
-0
lines changed

builder.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,24 @@ type Builder struct {
77

88
// NewBuilder creates a new Builder.
99
// A Builder is useful for creating multiple errors that share a common context, such as a request ID or service name, without repeatedly specifying the same options.
10+
//
11+
// Usage:
12+
// builder := goerr.NewBuilder(goerr.V("service", "auth"), goerr.V("request_id", "req123"))
13+
// err1 := builder.New("user not found")
14+
// err2 := builder.Wrap(dbErr, "query failed")
15+
// // Both errors will include service and request_id context
1016
func NewBuilder(options ...Option) *Builder {
1117
return &Builder{
1218
options: options,
1319
}
1420
}
1521

1622
// With copies the current Builder and adds a new key-value pair.
23+
//
24+
// Usage:
25+
// baseBuilder := goerr.NewBuilder(goerr.V("service", "auth"))
26+
// userBuilder := baseBuilder.With(goerr.V("user_id", "user123"))
27+
// err := userBuilder.New("access denied") // includes both service and user_id
1728
func (x *Builder) With(options ...Option) *Builder {
1829
newBuilder := &Builder{
1930
options: x.options[:],
@@ -23,13 +34,21 @@ func (x *Builder) With(options ...Option) *Builder {
2334
}
2435

2536
// New creates a new error with message
37+
//
38+
// Usage:
39+
// builder := goerr.NewBuilder(goerr.V("service", "auth"))
40+
// err := builder.New("authentication failed") // includes service context
2641
func (x *Builder) New(msg string, options ...Option) *Error {
2742
err := newError(append(x.options, options...)...)
2843
err.msg = msg
2944
return err
3045
}
3146

3247
// Wrap creates a new Error with caused error and add message.
48+
//
49+
// Usage:
50+
// builder := goerr.NewBuilder(goerr.V("service", "auth"))
51+
// err := builder.Wrap(dbErr, "database query failed") // wraps dbErr with service context
3352
func (x *Builder) Wrap(cause error, msg string, options ...Option) *Error {
3453
err := newError(append(x.options, options...)...)
3554
err.msg = msg

error.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ func New(msg string, options ...Option) *Error {
5353
}
5454

5555
// Wrap creates a new Error and add message.
56+
//
57+
// Usage:
58+
// baseErr := fmt.Errorf("connection failed")
59+
// err := goerr.Wrap(baseErr, "database operation failed",
60+
// goerr.V("host", "localhost"), goerr.V("port", 5432))
61+
// // Result: "database operation failed: connection failed" with context
5662
func Wrap(cause error, msg string, options ...Option) *Error {
5763
err := newError(options...)
5864
err.msg = msg
@@ -384,6 +390,11 @@ func (x *Error) mergedTags() tags {
384390

385391
// LogValue returns slog.Value for structured logging. It's implementation of slog.LogValuer.
386392
// https://pkg.go.dev/log/slog#LogValuer
393+
//
394+
// Usage:
395+
// err := goerr.New("operation failed", goerr.V("user_id", "user123"))
396+
// logger.Error("request failed", slog.Any("error", err))
397+
// // Automatically outputs structured log with error details, stack trace, and values
387398
func (x *Error) LogValue() slog.Value {
388399
if x == nil {
389400
return slog.AnyValue(nil)
@@ -447,6 +458,11 @@ func (x *Error) MarshalJSON() ([]byte, error) {
447458
//
448459
// If err is a *goerr.Error, it creates a new *Error that preserves the original stacktrace and adds the new options.
449460
// If err is a standard error, it wraps the error in a new *goerr.Error with a new stacktrace and adds the options.
461+
//
462+
// Usage:
463+
// originalErr := goerr.New("validation failed")
464+
// enrichedErr := goerr.With(originalErr, goerr.V("user_id", "user123"))
465+
// // originalErr is unchanged, enrichedErr has additional context
450466
func With(err error, options ...Option) *Error {
451467
if err == nil {
452468
return nil

error_test.go

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,3 +803,143 @@ func ExampleID() {
803803
}
804804
// Output: Error is a permission error
805805
}
806+
807+
// ExampleWith demonstrates how to add context to errors without modifying the original error.
808+
// goerr.With(err, options...) adds context without modifying the original error
809+
func ExampleWith() {
810+
// Create an original error
811+
originalErr := goerr.New("database connection failed")
812+
813+
// Add context without modifying the original error
814+
// Original error remains unchanged, new error with additional context is returned
815+
enrichedErr := goerr.With(originalErr,
816+
goerr.V("user_id", "user123"),
817+
goerr.V("component", "auth-service"),
818+
)
819+
820+
fmt.Println("Original:", originalErr.Error())
821+
fmt.Println("Enriched:", enrichedErr.Error())
822+
823+
// Verify original error has no additional context
824+
originalValues := goerr.Values(originalErr)
825+
fmt.Printf("Original has %d values\n", len(originalValues))
826+
827+
// Verify enriched error has the context
828+
enrichedValues := goerr.Values(enrichedErr)
829+
fmt.Printf("Enriched has %d values\n", len(enrichedValues))
830+
fmt.Printf("User ID: %s\n", enrichedValues["user_id"])
831+
832+
// Output:
833+
// Original: database connection failed
834+
// Enriched: database connection failed
835+
// Original has 0 values
836+
// Enriched has 2 values
837+
// User ID: user123
838+
}
839+
840+
// ExampleWrap demonstrates wrapping errors with additional context.
841+
// goerr.Wrap(cause, message, options...) wraps an existing error and adds additional information
842+
func ExampleWrap() {
843+
// Create a base error
844+
baseErr := fmt.Errorf("connection timeout")
845+
846+
// Wrap the error with additional context
847+
wrappedErr := goerr.Wrap(baseErr, "failed to connect to database",
848+
goerr.V("host", "localhost"),
849+
goerr.V("port", 5432),
850+
goerr.V("timeout", "30s"),
851+
)
852+
853+
fmt.Println("Error:", wrappedErr.Error())
854+
855+
// Access the wrapped values
856+
values := goerr.Values(wrappedErr)
857+
fmt.Printf("Host: %s\n", values["host"])
858+
fmt.Printf("Port: %v\n", values["port"])
859+
860+
// Check if the original error is preserved
861+
if errors.Is(wrappedErr, baseErr) {
862+
fmt.Println("Original error is preserved")
863+
}
864+
865+
// Output:
866+
// Error: failed to connect to database: connection timeout
867+
// Host: localhost
868+
// Port: 5432
869+
// Original error is preserved
870+
}
871+
872+
// ExampleBuilder demonstrates using Builder pattern for creating errors with shared context.
873+
// goerr.NewBuilder(options...) creates a builder, .With(options...) extends it, .New(msg) / .Wrap(err, msg) creates errors
874+
func ExampleBuilder() {
875+
// Create a builder with common context for a request
876+
builder := goerr.NewBuilder(
877+
goerr.V("service", "user-service"),
878+
goerr.V("request_id", "req-12345"),
879+
)
880+
881+
// Create errors using the builder - shared context is automatically included
882+
err1 := builder.New("user not found")
883+
err2 := builder.Wrap(fmt.Errorf("connection refused"), "database unavailable")
884+
885+
// All errors created by the builder include the shared context
886+
fmt.Println("Error 1:", err1.Error())
887+
fmt.Printf("Service: %s\n", goerr.Values(err1)["service"])
888+
889+
fmt.Println("Error 2:", err2.Error())
890+
fmt.Printf("Request ID: %s\n", goerr.Values(err2)["request_id"])
891+
892+
// Extend the builder with additional context
893+
extendedBuilder := builder.With(goerr.V("user_id", "user789"))
894+
err3 := extendedBuilder.New("permission denied")
895+
896+
fmt.Println("Error 3:", err3.Error())
897+
fmt.Printf("User ID: %s\n", goerr.Values(err3)["user_id"])
898+
899+
// Output:
900+
// Error 1: user not found
901+
// Service: user-service
902+
// Error 2: database unavailable: connection refused
903+
// Request ID: req-12345
904+
// Error 3: permission denied
905+
// User ID: user789
906+
}
907+
908+
// ExampleError_LogValue demonstrates structured logging with slog.LogValuer interface.
909+
// goerr.Error automatically implements slog.LogValuer for structured logging with slog.Any("error", err)
910+
func ExampleError_LogValue() {
911+
// Create an error with context
912+
err := goerr.New("operation failed",
913+
goerr.V("user_id", "user123"),
914+
goerr.V("operation", "delete_account"),
915+
)
916+
917+
// The error implements slog.LogValuer interface automatically
918+
logValue := err.LogValue()
919+
920+
// Extract some information from the log value for demonstration
921+
attrs := logValue.Group()
922+
var message, operation string
923+
for _, attr := range attrs {
924+
switch attr.Key {
925+
case "message":
926+
message = attr.Value.String()
927+
case "values":
928+
valueGroup := attr.Value.Group()
929+
for _, valueAttr := range valueGroup {
930+
if valueAttr.Key == "operation" {
931+
operation = valueAttr.Value.String()
932+
}
933+
}
934+
}
935+
}
936+
937+
fmt.Printf("Message: %s\n", message)
938+
fmt.Printf("Operation: %s\n", operation)
939+
fmt.Println("Structured logging ready")
940+
941+
// Output:
942+
// Message: operation failed
943+
// Operation: delete_account
944+
// Structured logging ready
945+
}

0 commit comments

Comments
 (0)